/*1* Copyright (C) 2017 - This file is part of libecc project2*3* Authors:4* Ryad BENADJILA <[email protected]>5* Arnaud EBALARD <[email protected]>6* Jean-Pierre FLORI <[email protected]>7*8* Contributors:9* Nicolas VIVET <[email protected]>10* Karim KHALFALLAH <[email protected]>11*12* This software is licensed under a dual BSD and GPL v2 license.13* See LICENSE file at the root folder of the project.14*/15#include <libecc/nn/nn_add.h>16#include <libecc/nn/nn.h>1718/*19* This module provides conditional addition and subtraction functions between20* two nn:21*22* o out = in1 +/- in2 if cnd is not zero.23* o out = in1 if cnd is zero.24*25* The time taken by the operation does not depend on cnd value, i.e. it is26* constant time for that specific factor, nor on the values of in1 and in2.27* It still depends on the maximal length of in1 and in2.28*29* Common addition and subtraction functions are derived from those conditional30* versions.31*/3233/*34* Conditionally adds 'in2' to 'in1' according to "cnd", storing the result35* in "out" and returning the carry in 'carry' parameter on success. This36* is the lowest level function for conditional addition. The function37* returns 0 on success, -1 on error.38*39* Note that unlike "usual" addition, the function is *in general* not40* commutative, i.e. "_nn_cnd_add(cnd, out, in1, in2)" is not equivalent41* to "_nn_cnd_add(cnd, out, in2, in1)". It is commutative though if "cnd"42* is not zero or 'in1' == 'in2'.43*44* Aliasing of inputs and output is possible. "out" is initialized if needed,45* that is if not aliased to 'in1' or 'in2'. The length of "out" is set to46* the maximal length of 'in1' and 'in2'. Note that both 'in1' and 'in2' will47* be read to this maximal length. As our memory managment model assumes that48* storage arrays only contains zeros past the "wlen" index, correct results49* will be produced. The length of 'out' is not normalized on return.50*51* The runtime of this function should not depend on:52* o the value of "cnd",53* o the data stored in 'in1' and 'in2'.54* It depends on:55* o the maximal length of 'in1' and 'in2'.56*57* This function is for internal use only.58*/59ATTRIBUTE_WARN_UNUSED_RET static int _nn_cnd_add(int cnd, nn_t out, nn_src_t in1, nn_src_t in2,60word_t *carry)61{62word_t tmp, carry1, carry2, _carry = WORD(0);63word_t mask = WORD_MASK_IFNOTZERO(cnd);64u8 i, loop_wlen;65int ret;6667MUST_HAVE((carry != NULL), ret, err);68ret = nn_check_initialized(in1); EG(ret, err);69ret = nn_check_initialized(in2); EG(ret, err);7071/* Handle aliasing */72loop_wlen = LOCAL_MAX(in1->wlen, in2->wlen);73if ((out != in1) && (out != in2)) {74ret = nn_init(out, (u16)(loop_wlen * WORD_BYTES)); EG(ret, err);75} else {76ret = nn_set_wlen(out, loop_wlen); EG(ret, err);77}7879/* Perform addition one word at a time, propagating the carry. */80for (i = 0; i < loop_wlen; i++) {81tmp = (word_t)(in1->val[i] + (in2->val[i] & mask));82carry1 = (word_t)(tmp < in1->val[i]);83out->val[i] = (word_t)(tmp + _carry);84carry2 = (word_t)(out->val[i] < tmp);85/* There is at most one carry going out. */86_carry = (word_t)(carry1 | carry2);87}8889(*carry) = _carry;9091err:92return ret;93}9495/*96* Conditionally adds 'in2' to 'in1' according to "cnd", storing the result97* in "out", including the potential carry overflowing past the maximal98* length of 'in1' and 'in2'. It is user responsibility to ensure that the99* resulting nn will not be higher than what can be supported. This is100* for instance guaranteed if both in1->wlen and in2->wlen are less than101* NN_MAX_WORD_LEN. Otherwise the function will error out which could leak102* information.103*104* Note that the length of the output depends the lengths of the inputs,105* but also on their values.106* It is the user responsibility to use this function carefully when107* constant time of an algorithm using this function is seeked.108* This choice was preferred above unconditionally increasing109* the length of the output by one, to ease the management of length110* explosion when multiple additions are performed.111* For finer carry propagation and length control the internal "_nn_cnd_add"112* function can be used.113*114* See "_nn_cnd_add" documentation above for further details.115*116* The function returns 0 on success, -1 on error.117*/118int nn_cnd_add(int cnd, nn_t out, nn_src_t in1, nn_src_t in2)119{120word_t carry;121int ret;122123ret = _nn_cnd_add(cnd, out, in1, in2, &carry); EG(ret, err);124125/* We cannot allow a non-zero carry if out->wlen is at its limit */126MUST_HAVE(((out->wlen != NN_MAX_WORD_LEN) || (!carry)), ret, err);127128if (out->wlen != NN_MAX_WORD_LEN) {129/*130* To maintain constant time, we perform carry addition in all131* cases. If carry is 0, no change is performed in practice,132* neither to 'out' value, nor to its length.133* Note that the length of the output can vary and make134* the time taken by further operations on it also vary.135*/136out->val[out->wlen] = carry;137out->wlen = (u8)(out->wlen + carry);138}139140err:141return ret;142}143144/*145* Unconditionally adds 'in2' to 'in1', storing the result in "out",146* including the potential carry overflowing past the maximal length of147* 'in1' and 'in2'. The function returns 0 on success, -1 on error.148*149* Note that the length of the output depends the lengths of the inputs,150* but also on their values.151* It is the user responsibility to use this function carefully when152* constant time of an algorithm using this function is seeked.153*154* See "_nn_cnd_add" documentation for further details.155*156*/157int nn_add(nn_t out, nn_src_t in1, nn_src_t in2)158{159return nn_cnd_add(1, out, in1, in2);160}161162/*163* Compute out = in1 + w where 'in1' is an initialized nn and 'w' a word. It is164* caller responsibility to ensure that the result will fit in a nn (This is165* for instance guaranteed if 'in1' wlen is less than NN_MAX_WORD_LEN). The166* function returns 0 on succes, -1 on error.167*168* The result is stored in 'out' parameter. 'out' is initialized if needed (i.e.169* in case aliasing is not used) and is not normalized on return.170*171* Note that the length of the output depends the lengths of the inputs,172* but also on their values.173* It is the user responsibility to use this function carefully when174* constant time of an algorithm using this function is seeked.175*176* This function is for internal use only.177*/178ATTRIBUTE_WARN_UNUSED_RET static int nn_add_word(nn_t out, nn_src_t in1, word_t w)179{180word_t carry, tmp;181u8 i, n_wlen;182int ret;183184ret = nn_check_initialized(in1); EG(ret, err);185186/* Handle aliasing */187n_wlen = in1->wlen;188if (out != in1) {189ret = nn_init(out, (u16)(n_wlen * WORD_BYTES)); EG(ret, err);190} else {191ret = nn_set_wlen(out, n_wlen); EG(ret, err);192}193194/* No matter its value, propagate the carry. */195carry = w;196for (i = 0; i < n_wlen; i++) {197tmp = (word_t)(in1->val[i] + carry);198carry = (word_t)(tmp < in1->val[i]);199out->val[i] = tmp;200}201202MUST_HAVE(((out->wlen != NN_MAX_WORD_LEN) || (!carry)), ret, err);203if (out->wlen != NN_MAX_WORD_LEN) {204/*205* To maintain constant time, we perform carry addition in all206* cases. If carry is 0, no change is performed in practice,207* neither to 'out' value, nor to its length.208* Note that the length of the output can vary and make209* the time taken by further operations on it will vary.210*/211out->val[out->wlen] = carry;212out->wlen = (u8)(out->wlen + carry);213}214215err:216return ret;217}218219/*220* Compute out = in1 + 1. Aliasing is supported i.e. nn_inc(in1, in1) works as221* expected and provides in1++. It is caller responsibility to ensure that the222* result will fit in a nn (This is for instance guaranteed if 'in1' wlen is223* less than NN_MAX_WORD_LEN). The function returns 0 on success, -1 on error.224*225* Note that the length of the output depends the lengths of the inputs,226* but also on their values.227* It is the user responsibility to use this function carefully when228* constant time of an algorithm using this function is seeked.229*/230int nn_inc(nn_t out, nn_src_t in1)231{232return nn_add_word(out, in1, WORD(1));233}234235/*236* Conditionally subtracts 'in2' from 'in1' according to "cnd",237* storing the result in "out":238* o out = in1 - in2 if cnd is not zero.239* o out = in1 if cnd is zero.240*241* 'in1' and 'in2' must point to initialized nn, such that the value of 'in1'242* is larger than 'in2'. Aliasing is supported, i.e. 'out' can point to the243* same nn as 'in1' or 'in2'. If aliasing is not used, 'out' is initialized by244* the function. The length of 'out' is set to the length of 'in1'245* and is not normalized on return.246*247* The function returns 0 on success, -1 on error.248*/249int nn_cnd_sub(int cnd, nn_t out, nn_src_t in1, nn_src_t in2)250{251word_t tmp, borrow1, borrow2, borrow = WORD(0);252word_t mask = WORD_MASK_IFNOTZERO(cnd);253u8 loop_wlen, i;254int ret;255256ret = nn_check_initialized(in1); EG(ret, err);257ret = nn_check_initialized(in2); EG(ret, err);258259/* Handle aliasing */260loop_wlen = LOCAL_MAX(in1->wlen, in2->wlen);261if ((out != in1) && (out != in2)) {262ret = nn_init(out, (u16)(loop_wlen * WORD_BYTES)); EG(ret, err);263} else {264ret = nn_set_wlen(out, in1->wlen); EG(ret, err);265}266267/* Perform subtraction one word at a time, propagating the borrow. */268for (i = 0; i < loop_wlen; i++) {269tmp = (word_t)(in1->val[i] - (in2->val[i] & mask));270borrow1 = (word_t)(tmp > in1->val[i]);271out->val[i] = (word_t)(tmp - borrow);272borrow2 = (word_t)(out->val[i] > tmp);273/* There is at most one borrow going out. */274borrow = (word_t)(borrow1 | borrow2);275}276277/* We only support the in1 >= in2 case */278ret = (borrow != WORD(0)) ? -1 : 0;279280err:281return ret;282}283284/* Same as the one above, but the subtraction is performed unconditionally. */285int nn_sub(nn_t out, nn_src_t in1, nn_src_t in2)286{287return nn_cnd_sub(1, out, in1, in2);288}289290/*291* Compute out = in1 - 1 where in1 is a *positive* integer. Aliasing is292* supported i.e. nn_dec(A, A) works as expected and provides A -= 1.293* The function returns 0 on success, -1 on error.294*/295int nn_dec(nn_t out, nn_src_t in1)296{297const word_t w = WORD(1);298word_t tmp, borrow;299u8 n_wlen, i;300int ret;301302ret = nn_check_initialized(in1); EG(ret, err);303n_wlen = in1->wlen;304ret = nn_set_wlen(out, n_wlen); EG(ret, err);305306/* Perform subtraction w/ provided word and propagate the borrow */307borrow = w;308for (i = 0; i < n_wlen; i++) {309tmp = (word_t)(in1->val[i] - borrow);310borrow = (word_t)(tmp > in1->val[i]);311out->val[i] = tmp;312}313314ret = (borrow != WORD(0)) ? -1 : 0;315316err:317return ret;318}319320/*321* The following functions handle modular arithmetic. Our outputs sizes do not322* need a "normalization" since everything will be bounded by the modular number323* size.324*325* Warning: the following functions are only useful when the inputs are < p,326* i.e. we suppose that the input are already reduced modulo p. These primitives327* are mostly useful for the Fp layer. Even though they give results when328* applied to inputs >= p, there is no guarantee that the result is indeed < p329* or correct whatsoever.330*/331332/*333* Compute out = in1 + in2 mod p. The function returns 0 on success, -1 on334* error.335*336* Aliasing not fully supported, for internal use only.337*/338static int _nn_mod_add(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)339{340int ret, larger, cmp;341342ret = nn_check_initialized(in1); EG(ret, err);343ret = nn_check_initialized(in2); EG(ret, err);344ret = nn_check_initialized(p); EG(ret, err);345MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */346SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */347SHOULD_HAVE((!nn_cmp(in2, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */348349ret = nn_add(out, in1, in2); EG(ret, err);350/*351* If previous addition extends out->wlen, this may have an effect on352* computation time of functions below. For that reason, we always353* normalize out->wlen to p->wlen + 1. Its length is set to that of354* p after the computations.355*356* We could also use _nn_cnd_add to catch the carry and deal357* with p's of size NN_MAX_WORD_LEN.358* It is still painful because we have no constraint on the lengths359* of in1 and in2 so getting a carry out does not necessarily mean360* that the sum is larger than p...361*/362ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err);363ret = nn_cmp(out, p, &cmp); EG(ret, err);364larger = (cmp >= 0);365ret = nn_cnd_sub(larger, out, out, p); EG(ret, err);366ret = nn_set_wlen(out, p->wlen);367368err:369return ret;370}371372/*373* Compute out = in1 + in2 mod p. The function returns 0 on success, -1 on374* error.375*376* Aliasing is supported.377*/378int nn_mod_add(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)379{380int ret;381382if(out == p){383nn p_cpy;384p_cpy.magic = WORD(0);385386ret = nn_copy(&p_cpy, p); EG(ret, err1);387ret = _nn_mod_add(out, in1, in2, &p_cpy);388389err1:390nn_uninit(&p_cpy);391EG(ret, err);392}393else{394ret = _nn_mod_add(out, in1, in2, p);395}396397err:398return ret;399}400401/*402* Compute out = in1 + 1 mod p. The function returns 0 on success, -1 on error.403*404* Aliasing not fully supported, for internal use only.405*/406static int _nn_mod_inc(nn_t out, nn_src_t in1, nn_src_t p)407{408int larger, ret, cmp;409410ret = nn_check_initialized(in1); EG(ret, err);411ret = nn_check_initialized(p); EG(ret, err);412MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */413SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */414415ret = nn_inc(out, in1); EG(ret, err);416ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); /* see comment in nn_mod_add() */417ret = nn_cmp(out, p, &cmp); EG(ret, err);418larger = (cmp >= 0);419ret = nn_cnd_sub(larger, out, out, p); EG(ret, err);420ret = nn_set_wlen(out, p->wlen);421422err:423return ret;424}425426/*427* Compute out = in1 + 1 mod p. The function returns 0 on success, -1 on error.428*429* Aliasing supported.430*/431int nn_mod_inc(nn_t out, nn_src_t in1, nn_src_t p)432{433int ret;434435if(out == p){436nn p_cpy;437p_cpy.magic = WORD(0);438439ret = nn_copy(&p_cpy, p); EG(ret, err1);440ret = _nn_mod_inc(out, in1, &p_cpy);441442err1:443nn_uninit(&p_cpy);444EG(ret, err);445}446else{447ret = _nn_mod_inc(out, in1, p);448}449450err:451return ret;452453}454455/*456* Compute out = in1 - in2 mod p. The function returns 0 on success, -1 on457* error.458*459* Aliasing not supported, for internal use only.460*/461static int _nn_mod_sub(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)462{463int smaller, ret, cmp;464nn_src_t in2_;465nn in2_cpy;466in2_cpy.magic = WORD(0);467468ret = nn_check_initialized(in1); EG(ret, err);469ret = nn_check_initialized(in2); EG(ret, err);470ret = nn_check_initialized(p); EG(ret, err);471MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */472SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */473SHOULD_HAVE((!nn_cmp(in2, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */474475/* Handle the case where in2 and out are aliased */476if (in2 == out) {477ret = nn_copy(&in2_cpy, in2); EG(ret, err);478in2_ = &in2_cpy;479} else {480ret = nn_init(&in2_cpy, 0); EG(ret, err);481in2_ = in2;482}483484/* The below trick is used to avoid handling of "negative" numbers. */485ret = nn_cmp(in1, in2_, &cmp); EG(ret, err);486smaller = (cmp < 0);487ret = nn_cnd_add(smaller, out, in1, p); EG(ret, err);488ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err);/* See Comment in nn_mod_add() */489ret = nn_sub(out, out, in2_); EG(ret, err);490ret = nn_set_wlen(out, p->wlen);491492err:493nn_uninit(&in2_cpy);494495return ret;496}497498/*499* Compute out = in1 - in2 mod p. The function returns 0 on success, -1 on500* error.501*502* Aliasing supported.503*/504int nn_mod_sub(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)505{506int ret;507508if(out == p){509nn p_cpy;510p_cpy.magic = WORD(0);511512ret = nn_copy(&p_cpy, p); EG(ret, err1);513ret = _nn_mod_sub(out, in1, in2, &p_cpy);514515err1:516nn_uninit(&p_cpy);517EG(ret, err);518}519else{520ret = _nn_mod_sub(out, in1, in2, p);521}522523err:524return ret;525}526527/*528* Compute out = in1 - 1 mod p. The function returns 0 on success, -1 on error529*530* Aliasing not supported, for internal use only.531*/532static int _nn_mod_dec(nn_t out, nn_src_t in1, nn_src_t p)533{534int ret, iszero, cmp;535536ret = nn_check_initialized(in1); EG(ret, err);537ret = nn_check_initialized(p); EG(ret, err);538MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */539FORCE_USED_VAR(cmp); /* nop to silence possible warning with macro below */540SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE; Documented above */541542/* The below trick is used to avoid handling of "negative" numbers. */543ret = nn_iszero(in1, &iszero); EG(ret, err);544ret = nn_cnd_add(iszero, out, in1, p); EG(ret, err);545ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); /* See Comment in nn_mod_add() */546ret = nn_dec(out, out); EG(ret, err);547ret = nn_set_wlen(out, p->wlen);548549err:550return ret;551}552553/*554* Compute out = in1 - 1 mod p. The function returns 0 on success, -1 on error555*556* Aliasing supported.557*/558int nn_mod_dec(nn_t out, nn_src_t in1, nn_src_t p)559{560int ret;561562if(out == p){563nn p_cpy;564p_cpy.magic = WORD(0);565566ret = nn_copy(&p_cpy, p); EG(ret, err1);567ret = _nn_mod_dec(out, in1, &p_cpy);568569err1:570nn_uninit(&p_cpy);571EG(ret, err);572}573else{574ret = _nn_mod_dec(out, in1, p);575}576577err:578return ret;579}580581/*582* Compute out = -in mod p. The function returns 0 on success, -1 on error.583* Because we only support positive integers, we compute584* out = p - in (except when value is 0).585*586* We suppose that in is already reduced modulo p.587*588* Aliasing is supported.589*590*/591int nn_mod_neg(nn_t out, nn_src_t in, nn_src_t p)592{593int ret, cmp, iszero;594595FORCE_USED_VAR(cmp);596597ret = nn_check_initialized(in); EG(ret, err);598ret = nn_check_initialized(p); EG(ret, err);599600SHOULD_HAVE((!nn_cmp(in, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE; Documented above */601602ret = nn_iszero(in, &iszero); EG(ret, err);603if (iszero) {604ret = nn_init(out, 0); EG(ret, err);605ret = nn_zero(out); EG(ret, err);606} else {607ret = nn_sub(out, p, in); EG(ret, err);608}609610err:611return ret;612}613614615