/*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_modinv.h>16#include <libecc/nn/nn_div_public.h>17#include <libecc/nn/nn_logical.h>18#include <libecc/nn/nn_add.h>19#include <libecc/nn/nn_mod_pow.h>20#include <libecc/nn/nn.h>21/* Include the "internal" header as we use non public API here */22#include "../nn/nn_mul.h"2324/*25* Compute out = x^-1 mod m, i.e. out such that (out * x) = 1 mod m26* out is initialized by the function, i.e. caller needs not initialize27* it; only provide the associated storage space. Done in *constant28* time* if underlying routines are.29*30* Asserts that m is odd and that x is smaller than m.31* This second condition is not strictly necessary,32* but it allows to perform all operations on nn's of the same length,33* namely the length of m.34*35* Uses a binary xgcd algorithm,36* only keeps track of coefficient for inverting x,37* and performs reduction modulo m at each step.38*39* This does not normalize out on return.40*41* 0 is returned on success (everything went ok and x has reciprocal), -142* on error or if x has no reciprocal. On error, out is not meaningful.43*44* The function is an internal helper: caller MUST check params have been45* initialized, i.e. this is not done by the function.46*47*/48ATTRIBUTE_WARN_UNUSED_RET static int _nn_modinv_odd(nn_t out, nn_src_t x, nn_src_t m)49{50int isodd, swap, smaller, ret, cmp, iszero, tmp_isodd;51nn a, b, u, tmp, mp1d2;52nn_t uu = out;53bitcnt_t cnt;54a.magic = b.magic = u.magic = tmp.magic = mp1d2.magic = WORD(0);5556ret = nn_init(out, 0); EG(ret, err);57ret = nn_init(&a, (u16)(m->wlen * WORD_BYTES)); EG(ret, err);58ret = nn_init(&b, (u16)(m->wlen * WORD_BYTES)); EG(ret, err);59ret = nn_init(&u, (u16)(m->wlen * WORD_BYTES)); EG(ret, err);60ret = nn_init(&mp1d2, (u16)(m->wlen * WORD_BYTES)); EG(ret, err);61/*62* Temporary space needed to only deal with positive stuff.63*/64ret = nn_init(&tmp, (u16)(m->wlen * WORD_BYTES)); EG(ret, err);6566MUST_HAVE((!nn_isodd(m, &isodd)) && isodd, ret, err);67MUST_HAVE((!nn_cmp(x, m, &cmp)) && (cmp < 0), ret, err);68MUST_HAVE((!nn_iszero(x, &iszero)) && (!iszero), ret, err);6970/*71* Maintain:72*73* a = u * x (mod m)74* b = uu * x (mod m)75*76* and b odd at all times. Initially,77*78* a = x, u = 179* b = m, uu = 080*/81ret = nn_copy(&a, x); EG(ret, err);82ret = nn_set_wlen(&a, m->wlen); EG(ret, err);83ret = nn_copy(&b, m); EG(ret, err);84ret = nn_one(&u); EG(ret, err);85ret = nn_zero(uu); EG(ret, err);8687/*88* The lengths of u and uu should not affect constant timeness but it89* does not hurt to set them already.90* They will always be strictly smaller than m.91*/92ret = nn_set_wlen(&u, m->wlen); EG(ret, err);93ret = nn_set_wlen(uu, m->wlen); EG(ret, err);9495/*96* Precompute inverse of 2 mod m:97* 2^-1 = (m+1)/298* computed as (m >> 1) + 1.99*/100ret = nn_rshift_fixedlen(&mp1d2, m, 1); EG(ret, err);101102ret = nn_inc(&mp1d2, &mp1d2); EG(ret, err); /* no carry can occur here103because of prev. shift */104105cnt = (bitcnt_t)((a.wlen + b.wlen) * WORD_BITS);106while (cnt > 0) {107cnt = (bitcnt_t)(cnt - 1);108/*109* Always maintain b odd. The logic of the iteration is as110* follows.111*/112113/*114* For a, b:115*116* odd = a & 1117* swap = odd & (a < b)118* if (swap)119* swap(a, b)120* if (odd)121* a -= b122* a /= 2123*/124125MUST_HAVE((!nn_isodd(&b, &tmp_isodd)) && tmp_isodd, ret, err);126127ret = nn_isodd(&a, &isodd); EG(ret, err);128ret = nn_cmp(&a, &b, &cmp); EG(ret, err);129swap = isodd & (cmp == -1);130131ret = nn_cnd_swap(swap, &a, &b); EG(ret, err);132ret = nn_cnd_sub(isodd, &a, &a, &b); EG(ret, err);133134MUST_HAVE((!nn_isodd(&a, &tmp_isodd)) && (!tmp_isodd), ret, err); /* a is now even */135136ret = nn_rshift_fixedlen(&a, &a, 1); EG(ret, err);/* division by 2 */137138/*139* For u, uu:140*141* if (swap)142* swap u, uu143* smaller = (u < uu)144* if (odd)145* if (smaller)146* u += m - uu147* else148* u -= uu149* u >>= 1150* if (u was odd)151* u += (m+1)/2152*/153ret = nn_cnd_swap(swap, &u, uu); EG(ret, err);154155/* This parameter is used to avoid handling negative numbers. */156ret = nn_cmp(&u, uu, &cmp); EG(ret, err);157smaller = (cmp == -1);158159/* Computation of 'm - uu' can always be performed. */160ret = nn_sub(&tmp, m, uu); EG(ret, err);161162/* Selection btw 'm-uu' and '-uu' is made by the following function calls. */163ret = nn_cnd_add(isodd & smaller, &u, &u, &tmp); EG(ret, err); /* no carry can occur as 'u+(m-uu) = m-(uu-u) < m' */164ret = nn_cnd_sub(isodd & (!smaller), &u, &u, uu); EG(ret, err);165166/* Divide u by 2 */167ret = nn_isodd(&u, &isodd); EG(ret, err);168ret = nn_rshift_fixedlen(&u, &u, 1); EG(ret, err);169ret = nn_cnd_add(isodd, &u, &u, &mp1d2); EG(ret, err); /* no carry can occur as u=1+u' with u'<m-1 and u' even so u'/2+(m+1)/2<(m-1)/2+(m+1)/2=m */170171MUST_HAVE((!nn_cmp(&u, m, &cmp)) && (cmp < 0), ret, err);172MUST_HAVE((!nn_cmp(uu, m, &cmp)) && (cmp < 0), ret, err);173174/*175* As long as a > 0, the quantity176* (bitsize of a) + (bitsize of b)177* is reduced by at least one bit per iteration,178* hence after (bitsize of x) + (bitsize of m) - 1179* iterations we surely have a = 0. Then b = gcd(x, m)180* and if b = 1 then also uu = x^{-1} (mod m).181*/182}183184MUST_HAVE((!nn_iszero(&a, &iszero)) && iszero, ret, err);185186/* Check that gcd is one. */187ret = nn_cmp_word(&b, WORD(1), &cmp); EG(ret, err);188189/* If not, set "inverse" to zero. */190ret = nn_cnd_sub(cmp != 0, uu, uu, uu); EG(ret, err);191192ret = cmp ? -1 : 0;193194err:195nn_uninit(&a);196nn_uninit(&b);197nn_uninit(&u);198nn_uninit(&mp1d2);199nn_uninit(&tmp);200201PTR_NULLIFY(uu);202203return ret;204}205206/*207* Same as above without restriction on m.208* No attempt to make it constant time.209* Uses the above constant-time binary xgcd when m is odd210* and a not constant time plain Euclidean xgcd when m is even.211*212* _out parameter need not be initialized; this will be done by the function.213* x and m must be initialized nn.214*215* Return -1 on error or if if x has no reciprocal modulo m. out is zeroed.216* Return 0 if x has reciprocal modulo m.217*218* The function supports aliasing.219*/220int nn_modinv(nn_t _out, nn_src_t x, nn_src_t m)221{222int sign, ret, cmp, isodd, isone;223nn_t x_mod_m;224nn u, v, out; /* Out to support aliasing */225out.magic = u.magic = v.magic = WORD(0);226227ret = nn_check_initialized(x); EG(ret, err);228ret = nn_check_initialized(m); EG(ret, err);229230/* Initialize out */231ret = nn_init(&out, 0); EG(ret, err);232ret = nn_isodd(m, &isodd); EG(ret, err);233if (isodd) {234ret = nn_cmp(x, m, &cmp); EG(ret, err);235if (cmp >= 0) {236/*237* If x >= m, (x^-1) mod m = ((x mod m)^-1) mod m238* Hence, compute x mod m. In order to avoid239* additional stack usage, we use 'u' (not240* already useful at that point in the function).241*/242x_mod_m = &u;243ret = nn_mod(x_mod_m, x, m); EG(ret, err);244ret = _nn_modinv_odd(&out, x_mod_m, m); EG(ret, err);245} else {246ret = _nn_modinv_odd(&out, x, m); EG(ret, err);247}248ret = nn_copy(_out, &out);249goto err;250}251252/* Now m is even */253ret = nn_isodd(x, &isodd); EG(ret, err);254MUST_HAVE(isodd, ret, err);255256ret = nn_init(&u, 0); EG(ret, err);257ret = nn_init(&v, 0); EG(ret, err);258ret = nn_xgcd(&out, &u, &v, x, m, &sign); EG(ret, err);259ret = nn_isone(&out, &isone); EG(ret, err);260MUST_HAVE(isone, ret, err);261262ret = nn_mod(&out, &u, m); EG(ret, err);263if (sign == -1) {264ret = nn_sub(&out, m, &out); EG(ret, err);265}266ret = nn_copy(_out, &out);267268err:269nn_uninit(&out);270nn_uninit(&u);271nn_uninit(&v);272273PTR_NULLIFY(x_mod_m);274275return ret;276}277278/*279* Compute (A - B) % 2^(storagebitsizeof(B) + 1). A and B must be initialized nn.280* the function is an internal helper and does not verify params have been281* initialized; this must be done by the caller. No assumption on A and B values282* such as A >= B. Done in *constant time. Returns 0 on success, -1 on error.283*/284ATTRIBUTE_WARN_UNUSED_RET static inline int _nn_sub_mod_2exp(nn_t A, nn_src_t B)285{286u8 Awlen = A->wlen;287int ret;288289ret = nn_set_wlen(A, (u8)(Awlen + 1)); EG(ret, err);290291/* Make sure A > B */292/* NOTE: A->wlen - 1 is not an issue here thant to the nn_set_wlen above */293A->val[A->wlen - 1] = WORD(1);294ret = nn_sub(A, A, B); EG(ret, err);295296/* The artificial word will be cleared in the following function call */297ret = nn_set_wlen(A, Awlen);298299err:300return ret;301}302303/*304* Invert x modulo 2^exp using Hensel lifting. Returns 0 on success, -1 on305* error. On success, x_isodd is 1 if x is odd, 0 if it is even.306* Please note that the result is correct (inverse of x) only when x is prime307* to 2^exp, i.e. x is odd (x_odd is 1).308*309* Operations are done in *constant time*.310*311* Aliasing is supported.312*/313int nn_modinv_2exp(nn_t _out, nn_src_t x, bitcnt_t exp, int *x_isodd)314{315bitcnt_t cnt;316u8 exp_wlen = (u8)BIT_LEN_WORDS(exp);317bitcnt_t exp_cnt = exp % WORD_BITS;318word_t mask = (word_t)((exp_cnt == 0) ? WORD_MASK : (word_t)((WORD(1) << exp_cnt) - WORD(1)));319nn tmp_sqr, tmp_mul;320/* for aliasing */321int isodd, ret;322nn out;323out.magic = tmp_sqr.magic = tmp_mul.magic = WORD(0);324325MUST_HAVE((x_isodd != NULL), ret, err);326ret = nn_check_initialized(x); EG(ret, err);327ret = nn_check_initialized(_out); EG(ret, err);328329ret = nn_init(&out, 0); EG(ret, err);330ret = nn_init(&tmp_sqr, 0); EG(ret, err);331ret = nn_init(&tmp_mul, 0); EG(ret, err);332ret = nn_isodd(x, &isodd); EG(ret, err);333if (exp == (bitcnt_t)0){334/* Specific case of zero exponent, output 0 */335(*x_isodd) = isodd;336goto err;337}338if (!isodd) {339ret = nn_zero(_out); EG(ret, err);340(*x_isodd) = 0;341goto err;342}343344/*345* Inverse modulo 2.346*/347cnt = 1;348ret = nn_one(&out); EG(ret, err);349350/*351* Inverse modulo 2^(2^i) <= 2^WORD_BITS.352* Assumes WORD_BITS is a power of two.353*/354for (; cnt < WORD_MIN(WORD_BITS, exp); cnt = (bitcnt_t)(cnt << 1)) {355ret = nn_sqr_low(&tmp_sqr, &out, out.wlen); EG(ret, err);356ret = nn_mul_low(&tmp_mul, &tmp_sqr, x, out.wlen); EG(ret, err);357ret = nn_lshift_fixedlen(&out, &out, 1); EG(ret, err);358359/*360* Allowing "negative" results for a subtraction modulo361* a power of two would allow to use directly:362* nn_sub(out, out, tmp_mul)363* which is always negative in ZZ except when x is one.364*365* Another solution is to add the opposite of tmp_mul.366* nn_modopp_2exp(tmp_mul, tmp_mul);367* nn_add(out, out, tmp_mul);368*369* The current solution is to add a sufficiently large power370* of two to out unconditionally to absorb the potential371* borrow. The result modulo 2^(2^i) is correct whether the372* borrow occurs or not.373*/374ret = _nn_sub_mod_2exp(&out, &tmp_mul); EG(ret, err);375}376377/*378* Inverse modulo 2^WORD_BITS < 2^(2^i) < 2^exp.379*/380for (; cnt < ((exp + 1) >> 1); cnt = (bitcnt_t)(cnt << 1)) {381ret = nn_set_wlen(&out, (u8)(2 * out.wlen)); EG(ret, err);382ret = nn_sqr_low(&tmp_sqr, &out, out.wlen); EG(ret, err);383ret = nn_mul_low(&tmp_mul, &tmp_sqr, x, out.wlen); EG(ret, err);384ret = nn_lshift_fixedlen(&out, &out, 1); EG(ret, err);385ret = _nn_sub_mod_2exp(&out, &tmp_mul); EG(ret, err);386}387388/*389* Inverse modulo 2^(2^i + j) >= 2^exp.390*/391if (exp > WORD_BITS) {392ret = nn_set_wlen(&out, exp_wlen); EG(ret, err);393ret = nn_sqr_low(&tmp_sqr, &out, out.wlen); EG(ret, err);394ret = nn_mul_low(&tmp_mul, &tmp_sqr, x, out.wlen); EG(ret, err);395ret = nn_lshift_fixedlen(&out, &out, 1); EG(ret, err);396ret = _nn_sub_mod_2exp(&out, &tmp_mul); EG(ret, err);397}398399/*400* Inverse modulo 2^exp.401*/402out.val[exp_wlen - 1] &= mask;403404ret = nn_copy(_out, &out); EG(ret, err);405406(*x_isodd) = 1;407408err:409nn_uninit(&out);410nn_uninit(&tmp_sqr);411nn_uninit(&tmp_mul);412413return ret;414}415416/*417* Invert word w modulo m.418*419* The function supports aliasing.420*/421int nn_modinv_word(nn_t out, word_t w, nn_src_t m)422{423nn nn_tmp;424int ret;425nn_tmp.magic = WORD(0);426427ret = nn_init(&nn_tmp, 0); EG(ret, err);428ret = nn_set_word_value(&nn_tmp, w); EG(ret, err);429ret = nn_modinv(out, &nn_tmp, m);430431err:432nn_uninit(&nn_tmp);433434return ret;435}436437438/*439* Internal function for nn_modinv_fermat and nn_modinv_fermat_redc used440* hereafter.441*/442ATTRIBUTE_WARN_UNUSED_RET static int _nn_modinv_fermat_common(nn_t out, nn_src_t x, nn_src_t p, nn_t p_minus_two, int *lesstwo)443{444int ret, cmp, isodd;445nn two;446two.magic = WORD(0);447448/* Sanity checks on inputs */449ret = nn_check_initialized(x); EG(ret, err);450ret = nn_check_initialized(p); EG(ret, err);451/* NOTE: since this is an internal function, we are ensured that p_minus_two,452* two and regular are OK.453*/454455/* 0 is not invertible in any case */456ret = nn_iszero(x, &cmp); EG(ret, err);457if(cmp){458/* Zero the output and return an error */459ret = nn_init(out, 0); EG(ret, err);460ret = nn_zero(out); EG(ret, err);461ret = -1;462goto err;463}464465/* For p <= 2, p being prime either p = 1 or p = 2.466* When p = 2, only 1 has an inverse, if p = 1 no one has an inverse.467*/468(*lesstwo) = 0;469ret = nn_cmp_word(p, WORD(2), &cmp); EG(ret, err);470if(cmp == 0){471/* This is the p = 2 case, parity of x provides the result */472ret = nn_isodd(x, &isodd); EG(ret, err);473if(isodd){474/* x is odd, 1 is its inverse */475ret = nn_init(out, 0); EG(ret, err);476ret = nn_one(out); EG(ret, err);477ret = 0;478}479else{480/* x is even, no inverse. Zero the output */481ret = nn_init(out, 0); EG(ret, err);482ret = nn_zero(out); EG(ret, err);483ret = -1;484}485(*lesstwo) = 1;486goto err;487} else if (cmp < 0){488/* This is the p = 1 case, no inverse here: hence return an error */489/* Zero the output */490ret = nn_init(out, 0); EG(ret, err);491ret = nn_zero(out); EG(ret, err);492ret = -1;493(*lesstwo) = 1;494goto err;495}496497/* Else we compute (p-2) for the upper layer */498if(p != p_minus_two){499/* Handle aliasing of p and p_minus_two */500ret = nn_init(p_minus_two, 0); EG(ret, err);501}502503ret = nn_init(&two, 0); EG(ret, err);504ret = nn_set_word_value(&two, WORD(2)); EG(ret, err);505ret = nn_sub(p_minus_two, p, &two);506507err:508nn_uninit(&two);509510return ret;511}512513/*514* Invert NN x modulo p using Fermat's little theorem for our inversion:515*516* p prime means that:517* x^(p-1) = 1 mod (p)518* which means that x^(p-2) mod(p) is the modular inverse of x mod (p)519* for x != 0520*521* NOTE: the input hypothesis is that p is prime.522* XXX WARNING: using this function with p not prime will produce wrong523* results without triggering an error!524*525* The function returns 0 on success, -1 on error526* (e.g. if x has no inverse modulo p, i.e. x = 0).527*528* Aliasing is supported.529*/530int nn_modinv_fermat(nn_t out, nn_src_t x, nn_src_t p)531{532int ret, lesstwo;533nn p_minus_two;534p_minus_two.magic = WORD(0);535536/* Call our helper.537* NOTE: "marginal" cases where x = 0 and p <= 2 should be caught in this helper.538*/539ret = _nn_modinv_fermat_common(out, x, p, &p_minus_two, &lesstwo); EG(ret, err);540541if(!lesstwo){542/* Compute x^(p-2) mod (p) */543ret = nn_mod_pow(out, x, &p_minus_two, p);544}545546err:547nn_uninit(&p_minus_two);548549return ret;550}551552/*553* Invert NN x modulo m using Fermat's little theorem for our inversion.554*555* This is a version with already (pre)computed Montgomery coefficients.556*557* NOTE: the input hypothesis is that p is prime.558* XXX WARNING: using this function with p not prime will produce wrong559* results without triggering an error!560*561* The function returns 0 on success, -1 on error562* (e.g. if x has no inverse modulo p, i.e. x = 0).563*564* Aliasing is supported.565*/566int nn_modinv_fermat_redc(nn_t out, nn_src_t x, nn_src_t p, nn_src_t r, nn_src_t r_square, word_t mpinv)567{568int ret, lesstwo;569nn p_minus_two;570p_minus_two.magic = WORD(0);571572/* Call our helper.573* NOTE: "marginal" cases where x = 0 and p <= 2 should be caught in this helper.574*/575ret = _nn_modinv_fermat_common(out, x, p, &p_minus_two, &lesstwo); EG(ret, err);576577if(!lesstwo){578/* Compute x^(p-2) mod (p) using precomputed Montgomery coefficients as input */579ret = nn_mod_pow_redc(out, x, &p_minus_two, p, r, r_square, mpinv);580}581582err:583nn_uninit(&p_minus_two);584585return ret;586}587588589