1 /* 2 * Copyright (C) 2017 - 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 * Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr> 8 * 9 * Contributors: 10 * Nicolas VIVET <nicolas.vivet@ssi.gouv.fr> 11 * Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr> 12 * 13 * This software is licensed under a dual BSD and GPL v2 license. 14 * See LICENSE file at the root folder of the project. 15 */ 16 #include <libecc/nn/nn_add.h> 17 #include <libecc/nn/nn.h> 18 19 /* 20 * This module provides conditional addition and subtraction functions between 21 * two nn: 22 * 23 * o out = in1 +/- in2 if cnd is not zero. 24 * o out = in1 if cnd is zero. 25 * 26 * The time taken by the operation does not depend on cnd value, i.e. it is 27 * constant time for that specific factor, nor on the values of in1 and in2. 28 * It still depends on the maximal length of in1 and in2. 29 * 30 * Common addition and subtraction functions are derived from those conditional 31 * versions. 32 */ 33 34 /* 35 * Conditionally adds 'in2' to 'in1' according to "cnd", storing the result 36 * in "out" and returning the carry in 'carry' parameter on success. This 37 * is the lowest level function for conditional addition. The function 38 * returns 0 on success, -1 on error. 39 * 40 * Note that unlike "usual" addition, the function is *in general* not 41 * commutative, i.e. "_nn_cnd_add(cnd, out, in1, in2)" is not equivalent 42 * to "_nn_cnd_add(cnd, out, in2, in1)". It is commutative though if "cnd" 43 * is not zero or 'in1' == 'in2'. 44 * 45 * Aliasing of inputs and output is possible. "out" is initialized if needed, 46 * that is if not aliased to 'in1' or 'in2'. The length of "out" is set to 47 * the maximal length of 'in1' and 'in2'. Note that both 'in1' and 'in2' will 48 * be read to this maximal length. As our memory managment model assumes that 49 * storage arrays only contains zeros past the "wlen" index, correct results 50 * will be produced. The length of 'out' is not normalized on return. 51 * 52 * The runtime of this function should not depend on: 53 * o the value of "cnd", 54 * o the data stored in 'in1' and 'in2'. 55 * It depends on: 56 * o the maximal length of 'in1' and 'in2'. 57 * 58 * This function is for internal use only. 59 */ 60 ATTRIBUTE_WARN_UNUSED_RET static int _nn_cnd_add(int cnd, nn_t out, nn_src_t in1, nn_src_t in2, 61 word_t *carry) 62 { 63 word_t tmp, carry1, carry2, _carry = WORD(0); 64 word_t mask = WORD_MASK_IFNOTZERO(cnd); 65 u8 i, loop_wlen; 66 int ret; 67 68 MUST_HAVE((carry != NULL), ret, err); 69 ret = nn_check_initialized(in1); EG(ret, err); 70 ret = nn_check_initialized(in2); EG(ret, err); 71 72 /* Handle aliasing */ 73 loop_wlen = LOCAL_MAX(in1->wlen, in2->wlen); 74 if ((out != in1) && (out != in2)) { 75 ret = nn_init(out, (u16)(loop_wlen * WORD_BYTES)); EG(ret, err); 76 } else { 77 ret = nn_set_wlen(out, loop_wlen); EG(ret, err); 78 } 79 80 /* Perform addition one word at a time, propagating the carry. */ 81 for (i = 0; i < loop_wlen; i++) { 82 tmp = (word_t)(in1->val[i] + (in2->val[i] & mask)); 83 carry1 = (word_t)(tmp < in1->val[i]); 84 out->val[i] = (word_t)(tmp + _carry); 85 carry2 = (word_t)(out->val[i] < tmp); 86 /* There is at most one carry going out. */ 87 _carry = (word_t)(carry1 | carry2); 88 } 89 90 (*carry) = _carry; 91 92 err: 93 return ret; 94 } 95 96 /* 97 * Conditionally adds 'in2' to 'in1' according to "cnd", storing the result 98 * in "out", including the potential carry overflowing past the maximal 99 * length of 'in1' and 'in2'. It is user responsibility to ensure that the 100 * resulting nn will not be higher than what can be supported. This is 101 * for instance guaranteed if both in1->wlen and in2->wlen are less than 102 * NN_MAX_WORD_LEN. Otherwise the function will error out which could leak 103 * information. 104 * 105 * Note that the length of the output depends the lengths of the inputs, 106 * but also on their values. 107 * It is the user responsibility to use this function carefully when 108 * constant time of an algorithm using this function is seeked. 109 * This choice was preferred above unconditionally increasing 110 * the length of the output by one, to ease the management of length 111 * explosion when multiple additions are performed. 112 * For finer carry propagation and length control the internal "_nn_cnd_add" 113 * function can be used. 114 * 115 * See "_nn_cnd_add" documentation above for further details. 116 * 117 * The function returns 0 on success, -1 on error. 118 */ 119 int nn_cnd_add(int cnd, nn_t out, nn_src_t in1, nn_src_t in2) 120 { 121 word_t carry; 122 int ret; 123 124 ret = _nn_cnd_add(cnd, out, in1, in2, &carry); EG(ret, err); 125 126 /* We cannot allow a non-zero carry if out->wlen is at its limit */ 127 MUST_HAVE(((out->wlen != NN_MAX_WORD_LEN) || (!carry)), ret, err); 128 129 if (out->wlen != NN_MAX_WORD_LEN) { 130 /* 131 * To maintain constant time, we perform carry addition in all 132 * cases. If carry is 0, no change is performed in practice, 133 * neither to 'out' value, nor to its length. 134 * Note that the length of the output can vary and make 135 * the time taken by further operations on it also vary. 136 */ 137 out->val[out->wlen] = carry; 138 out->wlen = (u8)(out->wlen + carry); 139 } 140 141 err: 142 return ret; 143 } 144 145 /* 146 * Unconditionally adds 'in2' to 'in1', storing the result in "out", 147 * including the potential carry overflowing past the maximal length of 148 * 'in1' and 'in2'. The function returns 0 on success, -1 on error. 149 * 150 * Note that the length of the output depends the lengths of the inputs, 151 * but also on their values. 152 * It is the user responsibility to use this function carefully when 153 * constant time of an algorithm using this function is seeked. 154 * 155 * See "_nn_cnd_add" documentation for further details. 156 * 157 */ 158 int nn_add(nn_t out, nn_src_t in1, nn_src_t in2) 159 { 160 return nn_cnd_add(1, out, in1, in2); 161 } 162 163 /* 164 * Compute out = in1 + w where 'in1' is an initialized nn and 'w' a word. It is 165 * caller responsibility to ensure that the result will fit in a nn (This is 166 * for instance guaranteed if 'in1' wlen is less than NN_MAX_WORD_LEN). The 167 * function returns 0 on succes, -1 on error. 168 * 169 * The result is stored in 'out' parameter. 'out' is initialized if needed (i.e. 170 * in case aliasing is not used) and is not normalized on return. 171 * 172 * Note that the length of the output depends the lengths of the inputs, 173 * but also on their values. 174 * It is the user responsibility to use this function carefully when 175 * constant time of an algorithm using this function is seeked. 176 * 177 * This function is for internal use only. 178 */ 179 ATTRIBUTE_WARN_UNUSED_RET static int nn_add_word(nn_t out, nn_src_t in1, word_t w) 180 { 181 word_t carry, tmp; 182 u8 i, n_wlen; 183 int ret; 184 185 ret = nn_check_initialized(in1); EG(ret, err); 186 187 /* Handle aliasing */ 188 n_wlen = in1->wlen; 189 if (out != in1) { 190 ret = nn_init(out, (u16)(n_wlen * WORD_BYTES)); EG(ret, err); 191 } else { 192 ret = nn_set_wlen(out, n_wlen); EG(ret, err); 193 } 194 195 /* No matter its value, propagate the carry. */ 196 carry = w; 197 for (i = 0; i < n_wlen; i++) { 198 tmp = (word_t)(in1->val[i] + carry); 199 carry = (word_t)(tmp < in1->val[i]); 200 out->val[i] = tmp; 201 } 202 203 MUST_HAVE(((out->wlen != NN_MAX_WORD_LEN) || (!carry)), ret, err); 204 if (out->wlen != NN_MAX_WORD_LEN) { 205 /* 206 * To maintain constant time, we perform carry addition in all 207 * cases. If carry is 0, no change is performed in practice, 208 * neither to 'out' value, nor to its length. 209 * Note that the length of the output can vary and make 210 * the time taken by further operations on it will vary. 211 */ 212 out->val[out->wlen] = carry; 213 out->wlen = (u8)(out->wlen + carry); 214 } 215 216 err: 217 return ret; 218 } 219 220 /* 221 * Compute out = in1 + 1. Aliasing is supported i.e. nn_inc(in1, in1) works as 222 * expected and provides in1++. It is caller responsibility to ensure that the 223 * result will fit in a nn (This is for instance guaranteed if 'in1' wlen is 224 * less than NN_MAX_WORD_LEN). The function returns 0 on success, -1 on error. 225 * 226 * Note that the length of the output depends the lengths of the inputs, 227 * but also on their values. 228 * It is the user responsibility to use this function carefully when 229 * constant time of an algorithm using this function is seeked. 230 */ 231 int nn_inc(nn_t out, nn_src_t in1) 232 { 233 return nn_add_word(out, in1, WORD(1)); 234 } 235 236 /* 237 * Conditionally subtracts 'in2' from 'in1' according to "cnd", 238 * storing the result in "out": 239 * o out = in1 - in2 if cnd is not zero. 240 * o out = in1 if cnd is zero. 241 * 242 * 'in1' and 'in2' must point to initialized nn, such that the value of 'in1' 243 * is larger than 'in2'. Aliasing is supported, i.e. 'out' can point to the 244 * same nn as 'in1' or 'in2'. If aliasing is not used, 'out' is initialized by 245 * the function. The length of 'out' is set to the length of 'in1' 246 * and is not normalized on return. 247 * 248 * The function returns 0 on success, -1 on error. 249 */ 250 int nn_cnd_sub(int cnd, nn_t out, nn_src_t in1, nn_src_t in2) 251 { 252 word_t tmp, borrow1, borrow2, borrow = WORD(0); 253 word_t mask = WORD_MASK_IFNOTZERO(cnd); 254 u8 loop_wlen, i; 255 int ret; 256 257 ret = nn_check_initialized(in1); EG(ret, err); 258 ret = nn_check_initialized(in2); EG(ret, err); 259 260 /* Handle aliasing */ 261 loop_wlen = LOCAL_MAX(in1->wlen, in2->wlen); 262 if ((out != in1) && (out != in2)) { 263 ret = nn_init(out, (u16)(loop_wlen * WORD_BYTES)); EG(ret, err); 264 } else { 265 ret = nn_set_wlen(out, in1->wlen); EG(ret, err); 266 } 267 268 /* Perform subtraction one word at a time, propagating the borrow. */ 269 for (i = 0; i < loop_wlen; i++) { 270 tmp = (word_t)(in1->val[i] - (in2->val[i] & mask)); 271 borrow1 = (word_t)(tmp > in1->val[i]); 272 out->val[i] = (word_t)(tmp - borrow); 273 borrow2 = (word_t)(out->val[i] > tmp); 274 /* There is at most one borrow going out. */ 275 borrow = (word_t)(borrow1 | borrow2); 276 } 277 278 /* We only support the in1 >= in2 case */ 279 ret = (borrow != WORD(0)) ? -1 : 0; 280 281 err: 282 return ret; 283 } 284 285 /* Same as the one above, but the subtraction is performed unconditionally. */ 286 int nn_sub(nn_t out, nn_src_t in1, nn_src_t in2) 287 { 288 return nn_cnd_sub(1, out, in1, in2); 289 } 290 291 /* 292 * Compute out = in1 - 1 where in1 is a *positive* integer. Aliasing is 293 * supported i.e. nn_dec(A, A) works as expected and provides A -= 1. 294 * The function returns 0 on success, -1 on error. 295 */ 296 int nn_dec(nn_t out, nn_src_t in1) 297 { 298 const word_t w = WORD(1); 299 word_t tmp, borrow; 300 u8 n_wlen, i; 301 int ret; 302 303 ret = nn_check_initialized(in1); EG(ret, err); 304 n_wlen = in1->wlen; 305 ret = nn_set_wlen(out, n_wlen); EG(ret, err); 306 307 /* Perform subtraction w/ provided word and propagate the borrow */ 308 borrow = w; 309 for (i = 0; i < n_wlen; i++) { 310 tmp = (word_t)(in1->val[i] - borrow); 311 borrow = (word_t)(tmp > in1->val[i]); 312 out->val[i] = tmp; 313 } 314 315 ret = (borrow != WORD(0)) ? -1 : 0; 316 317 err: 318 return ret; 319 } 320 321 /* 322 * The following functions handle modular arithmetic. Our outputs sizes do not 323 * need a "normalization" since everything will be bounded by the modular number 324 * size. 325 * 326 * Warning: the following functions are only useful when the inputs are < p, 327 * i.e. we suppose that the input are already reduced modulo p. These primitives 328 * are mostly useful for the Fp layer. Even though they give results when 329 * applied to inputs >= p, there is no guarantee that the result is indeed < p 330 * or correct whatsoever. 331 */ 332 333 /* 334 * Compute out = in1 + in2 mod p. The function returns 0 on success, -1 on 335 * error. 336 * 337 * Aliasing not fully supported, for internal use only. 338 */ 339 static int _nn_mod_add(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p) 340 { 341 int ret, larger, cmp; 342 343 ret = nn_check_initialized(in1); EG(ret, err); 344 ret = nn_check_initialized(in2); EG(ret, err); 345 ret = nn_check_initialized(p); EG(ret, err); 346 MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */ 347 SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */ 348 SHOULD_HAVE((!nn_cmp(in2, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */ 349 350 ret = nn_add(out, in1, in2); EG(ret, err); 351 /* 352 * If previous addition extends out->wlen, this may have an effect on 353 * computation time of functions below. For that reason, we always 354 * normalize out->wlen to p->wlen + 1. Its length is set to that of 355 * p after the computations. 356 * 357 * We could also use _nn_cnd_add to catch the carry and deal 358 * with p's of size NN_MAX_WORD_LEN. 359 * It is still painful because we have no constraint on the lengths 360 * of in1 and in2 so getting a carry out does not necessarily mean 361 * that the sum is larger than p... 362 */ 363 ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); 364 ret = nn_cmp(out, p, &cmp); EG(ret, err); 365 larger = (cmp >= 0); 366 ret = nn_cnd_sub(larger, out, out, p); EG(ret, err); 367 ret = nn_set_wlen(out, p->wlen); 368 369 err: 370 return ret; 371 } 372 373 /* 374 * Compute out = in1 + in2 mod p. The function returns 0 on success, -1 on 375 * error. 376 * 377 * Aliasing is supported. 378 */ 379 int nn_mod_add(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p) 380 { 381 int ret; 382 383 if(out == p){ 384 nn p_cpy; 385 p_cpy.magic = WORD(0); 386 387 ret = nn_copy(&p_cpy, p); EG(ret, err1); 388 ret = _nn_mod_add(out, in1, in2, &p_cpy); 389 390 err1: 391 nn_uninit(&p_cpy); 392 EG(ret, err); 393 } 394 else{ 395 ret = _nn_mod_add(out, in1, in2, p); 396 } 397 398 err: 399 return ret; 400 } 401 402 /* 403 * Compute out = in1 + 1 mod p. The function returns 0 on success, -1 on error. 404 * 405 * Aliasing not fully supported, for internal use only. 406 */ 407 static int _nn_mod_inc(nn_t out, nn_src_t in1, nn_src_t p) 408 { 409 int larger, ret, cmp; 410 411 ret = nn_check_initialized(in1); EG(ret, err); 412 ret = nn_check_initialized(p); EG(ret, err); 413 MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */ 414 SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */ 415 416 ret = nn_inc(out, in1); EG(ret, err); 417 ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); /* see comment in nn_mod_add() */ 418 ret = nn_cmp(out, p, &cmp); EG(ret, err); 419 larger = (cmp >= 0); 420 ret = nn_cnd_sub(larger, out, out, p); EG(ret, err); 421 ret = nn_set_wlen(out, p->wlen); 422 423 err: 424 return ret; 425 } 426 427 /* 428 * Compute out = in1 + 1 mod p. The function returns 0 on success, -1 on error. 429 * 430 * Aliasing supported. 431 */ 432 int nn_mod_inc(nn_t out, nn_src_t in1, nn_src_t p) 433 { 434 int ret; 435 436 if(out == p){ 437 nn p_cpy; 438 p_cpy.magic = WORD(0); 439 440 ret = nn_copy(&p_cpy, p); EG(ret, err1); 441 ret = _nn_mod_inc(out, in1, &p_cpy); 442 443 err1: 444 nn_uninit(&p_cpy); 445 EG(ret, err); 446 } 447 else{ 448 ret = _nn_mod_inc(out, in1, p); 449 } 450 451 err: 452 return ret; 453 454 } 455 456 /* 457 * Compute out = in1 - in2 mod p. The function returns 0 on success, -1 on 458 * error. 459 * 460 * Aliasing not supported, for internal use only. 461 */ 462 static int _nn_mod_sub(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p) 463 { 464 int smaller, ret, cmp; 465 nn_src_t in2_; 466 nn in2_cpy; 467 in2_cpy.magic = WORD(0); 468 469 ret = nn_check_initialized(in1); EG(ret, err); 470 ret = nn_check_initialized(in2); EG(ret, err); 471 ret = nn_check_initialized(p); EG(ret, err); 472 MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */ 473 SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */ 474 SHOULD_HAVE((!nn_cmp(in2, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */ 475 476 /* Handle the case where in2 and out are aliased */ 477 if (in2 == out) { 478 ret = nn_copy(&in2_cpy, in2); EG(ret, err); 479 in2_ = &in2_cpy; 480 } else { 481 ret = nn_init(&in2_cpy, 0); EG(ret, err); 482 in2_ = in2; 483 } 484 485 /* The below trick is used to avoid handling of "negative" numbers. */ 486 ret = nn_cmp(in1, in2_, &cmp); EG(ret, err); 487 smaller = (cmp < 0); 488 ret = nn_cnd_add(smaller, out, in1, p); EG(ret, err); 489 ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err);/* See Comment in nn_mod_add() */ 490 ret = nn_sub(out, out, in2_); EG(ret, err); 491 ret = nn_set_wlen(out, p->wlen); 492 493 err: 494 nn_uninit(&in2_cpy); 495 496 return ret; 497 } 498 499 /* 500 * Compute out = in1 - in2 mod p. The function returns 0 on success, -1 on 501 * error. 502 * 503 * Aliasing supported. 504 */ 505 int nn_mod_sub(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p) 506 { 507 int ret; 508 509 if(out == p){ 510 nn p_cpy; 511 p_cpy.magic = WORD(0); 512 513 ret = nn_copy(&p_cpy, p); EG(ret, err1); 514 ret = _nn_mod_sub(out, in1, in2, &p_cpy); 515 516 err1: 517 nn_uninit(&p_cpy); 518 EG(ret, err); 519 } 520 else{ 521 ret = _nn_mod_sub(out, in1, in2, p); 522 } 523 524 err: 525 return ret; 526 } 527 528 /* 529 * Compute out = in1 - 1 mod p. The function returns 0 on success, -1 on error 530 * 531 * Aliasing not supported, for internal use only. 532 */ 533 static int _nn_mod_dec(nn_t out, nn_src_t in1, nn_src_t p) 534 { 535 int ret, iszero, cmp; 536 537 ret = nn_check_initialized(in1); EG(ret, err); 538 ret = nn_check_initialized(p); EG(ret, err); 539 MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */ 540 FORCE_USED_VAR(cmp); /* nop to silence possible warning with macro below */ 541 SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE; Documented above */ 542 543 /* The below trick is used to avoid handling of "negative" numbers. */ 544 ret = nn_iszero(in1, &iszero); EG(ret, err); 545 ret = nn_cnd_add(iszero, out, in1, p); EG(ret, err); 546 ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); /* See Comment in nn_mod_add() */ 547 ret = nn_dec(out, out); EG(ret, err); 548 ret = nn_set_wlen(out, p->wlen); 549 550 err: 551 return ret; 552 } 553 554 /* 555 * Compute out = in1 - 1 mod p. The function returns 0 on success, -1 on error 556 * 557 * Aliasing supported. 558 */ 559 int nn_mod_dec(nn_t out, nn_src_t in1, nn_src_t p) 560 { 561 int ret; 562 563 if(out == p){ 564 nn p_cpy; 565 p_cpy.magic = WORD(0); 566 567 ret = nn_copy(&p_cpy, p); EG(ret, err1); 568 ret = _nn_mod_dec(out, in1, &p_cpy); 569 570 err1: 571 nn_uninit(&p_cpy); 572 EG(ret, err); 573 } 574 else{ 575 ret = _nn_mod_dec(out, in1, p); 576 } 577 578 err: 579 return ret; 580 } 581 582 /* 583 * Compute out = -in mod p. The function returns 0 on success, -1 on error. 584 * Because we only support positive integers, we compute 585 * out = p - in (except when value is 0). 586 * 587 * We suppose that in is already reduced modulo p. 588 * 589 * Aliasing is supported. 590 * 591 */ 592 int nn_mod_neg(nn_t out, nn_src_t in, nn_src_t p) 593 { 594 int ret, cmp, iszero; 595 596 FORCE_USED_VAR(cmp); 597 598 ret = nn_check_initialized(in); EG(ret, err); 599 ret = nn_check_initialized(p); EG(ret, err); 600 601 SHOULD_HAVE((!nn_cmp(in, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE; Documented above */ 602 603 ret = nn_iszero(in, &iszero); EG(ret, err); 604 if (iszero) { 605 ret = nn_init(out, 0); EG(ret, err); 606 ret = nn_zero(out); EG(ret, err); 607 } else { 608 ret = nn_sub(out, p, in); EG(ret, err); 609 } 610 611 err: 612 return ret; 613 } 614