/*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_logical.h>16#include <libecc/nn/nn.h>1718/*19* nn_lshift_fixedlen: left logical shift in N, i.e. compute out = (in << cnt).20*21* Aliasing is possible for 'in' and 'out', i.e. x <<= cnt can be computed22* using nn_lshift_fixedlen(x, x, cnt).23*24* The function supports 'in' and 'out' parameters of differents sizes.25*26* The operation time of the function depends on the size of 'in' and27* 'out' parameters and the value of 'cnt'. It does not depend on the28* value of 'in'.29*30* It is to be noted that the function uses out->wlen as the31* upper limit for its work, i.e. bits shifted above out->wlen32* are lost (the NN size of the output is not modified).33*34* The function returns 0 on sucess, -1 on error.35*36* Aliasing supported.37*/38int nn_lshift_fixedlen(nn_t out, nn_src_t in, bitcnt_t cnt)39{40int ipos, opos, dec, ret;41bitcnt_t lshift, hshift;42u8 owlen, iwlen;4344ret = nn_check_initialized(in); EG(ret, err);45ret = nn_check_initialized(out); EG(ret, err);46owlen = out->wlen;47iwlen = in->wlen;4849dec = cnt / WORD_BITS;50hshift = cnt % WORD_BITS;51lshift = (bitcnt_t)(WORD_BITS - hshift);5253for (opos = owlen - 1; opos >= 0; opos--) {54word_t hipart = 0, lopart = 0;5556ipos = opos - dec - 1;57if ((ipos >= 0) && (ipos < iwlen)) {58lopart = WRSHIFT(in->val[ipos], lshift);59}6061ipos = opos - dec;62if ((ipos >= 0) && (ipos < iwlen)) {63hipart = WLSHIFT(in->val[ipos], hshift);64}6566out->val[opos] = hipart | lopart;67}6869err:70return ret;71}7273/*74* nn_lshift: left logical shift in N, i.e. compute out = (in << cnt).75*76* Aliasing is possible for 'in' and 'out', i.e. x <<= cnt can be computed77* using nn_lshift(x, x, cnt).78*79* The function supports 'in' and 'out' parameters of differents sizes.80*81* The operation time of the function depends on the size of 'in' and82* 'out' parameters and the value of 'cnt'. It does not depend on the83* value of 'in'.84*85* It is to be noted that the function computes the output bit length86* depending on the shift count and the input length, i.e. out bit length87* will be roughly in bit length plus cnt, maxed to NN_MAX_BIT_LEN.88*89* The function returns 0 on success, -1 on error.90*91* Aliasing supported.92*/93int nn_lshift(nn_t out, nn_src_t in, bitcnt_t cnt)94{95bitcnt_t lshift, hshift, blen;96int ipos, opos, dec, ret;97u8 owlen, iwlen;9899ret = nn_check_initialized(in); EG(ret, err);100iwlen = in->wlen;101102/* Initialize output if no aliasing is used */103if (out != in) {104ret = nn_init(out, 0); EG(ret, err);105}106107/* Adapt output length accordingly */108ret = nn_bitlen(in, &blen); EG(ret, err);109owlen = (u8)LOCAL_MIN(BIT_LEN_WORDS(cnt + blen),110BIT_LEN_WORDS(NN_MAX_BIT_LEN));111out->wlen = owlen;112113dec = cnt / WORD_BITS;114hshift = cnt % WORD_BITS;115lshift = (bitcnt_t)(WORD_BITS - hshift);116117for (opos = owlen - 1; opos >= 0; opos--) {118word_t hipart = 0, lopart = 0;119120ipos = opos - dec - 1;121if ((ipos >= 0) && (ipos < iwlen)) {122lopart = WRSHIFT(in->val[ipos], lshift);123}124125ipos = opos - dec;126if ((ipos >= 0) && (ipos < iwlen)) {127hipart = WLSHIFT(in->val[ipos], hshift);128}129130out->val[opos] = hipart | lopart;131}132133err:134return ret;135}136137/*138* nn_rshift_fixedlen: right logical shift in N, i.e. compute out = (in >> cnt).139*140* Aliasing is possible for 'in' and 'out', i.e. x >>= cnt can be computed141* using nn_rshift_fixedlen(x, x, cnt).142*143* The function supports 'in' and 'out' parameters of differents sizes.144*145* The operation time of the function depends on the size of 'in' and146* 'out' parameters and the value of 'cnt'. It does not depend on the147* value of 'in'.148* It is to be noted that the function uses out->wlen as the149* upper limit for its work, which means zeroes are shifted in while150* keeping the same NN output size.151*152* The function returns 0 on success, -1 on error.153*154* Aliasing supported.155*/156int nn_rshift_fixedlen(nn_t out, nn_src_t in, bitcnt_t cnt)157{158int ipos, opos, dec, ret;159bitcnt_t lshift, hshift;160u8 owlen, iwlen;161162ret = nn_check_initialized(in); EG(ret, err);163ret = nn_check_initialized(out); EG(ret, err);164owlen = out->wlen;165iwlen = in->wlen;166167dec = cnt / WORD_BITS;168lshift = cnt % WORD_BITS;169hshift = (bitcnt_t)(WORD_BITS - lshift);170171for (opos = 0; opos < owlen; opos++) {172word_t hipart = 0, lopart = 0;173174ipos = opos + dec;175if ((ipos >= 0) && (ipos < iwlen)) {176lopart = WRSHIFT(in->val[ipos], lshift);177}178179ipos = opos + dec + 1;180if ((ipos >= 0) && (ipos < iwlen)) {181hipart = WLSHIFT(in->val[ipos], hshift);182}183184out->val[opos] = hipart | lopart;185}186187err:188return ret;189}190191/*192* nn_rshift: right logical shift in N, i.e. compute out = (in >> cnt).193*194* Aliasing is possible for 'in' and 'out', i.e. x >>= cnt can be computed195* using nn_rshift_fixedlen(x, x, cnt).196*197* The function supports 'in' and 'out' parameters of differents sizes.198*199* The operation time of the function depends on the size of 'in' and200* 'out' parameters and the value of 'cnt'. It does not depend on the201* value of 'in'.202* It is to be noted that the function adapts the output size to203* the input size and the shift bit count, i.e. out bit lenth is roughly204* equal to input bit length minus cnt.205*206* The function returns 0 on success, -1 on error.207*208* Aliasing supported.209*/210int nn_rshift(nn_t out, nn_src_t in, bitcnt_t cnt)211{212int ipos, opos, dec, ret;213bitcnt_t lshift, hshift;214u8 owlen, iwlen;215bitcnt_t blen;216217ret = nn_check_initialized(in); EG(ret, err);218iwlen = in->wlen;219/* Initialize output if no aliasing is used */220if (out != in) {221ret = nn_init(out, 0); EG(ret, err);222}223224dec = cnt / WORD_BITS;225lshift = cnt % WORD_BITS;226hshift = (bitcnt_t)(WORD_BITS - lshift);227228/* Adapt output length accordingly */229ret = nn_bitlen(in, &blen); EG(ret, err);230if (cnt > blen) {231owlen = 0;232} else {233owlen = (u8)BIT_LEN_WORDS(blen - cnt);234}235/* Adapt output length in out */236out->wlen = owlen;237238for (opos = 0; opos < owlen; opos++) {239word_t hipart = 0, lopart = 0;240241ipos = opos + dec;242if ((ipos >= 0) && (ipos < iwlen)) {243lopart = WRSHIFT(in->val[ipos], lshift);244}245246ipos = opos + dec + 1;247if ((ipos >= 0) && (ipos < iwlen)) {248hipart = WLSHIFT(in->val[ipos], hshift);249}250251out->val[opos] = hipart | lopart;252}253254/*255* Zero the output upper part now that we don't need it anymore256* NB: as we cannot use our normalize helper here (since a consistency257* check is done on wlen and upper part), we have to do this manually258*/259for (opos = owlen; opos < NN_MAX_WORD_LEN; opos++) {260out->val[opos] = 0;261}262263err:264return ret;265}266267/*268* This function right rotates the input NN value by the value 'cnt' on the269* bitlen basis. The function does it in the following way; right rotation270* of x by cnt is "simply": (x >> cnt) ^ (x << (bitlen - cnt))271*272* The function returns 0 on success, -1 on error.273*274* Aliasing supported.275*/276int nn_rrot(nn_t out, nn_src_t in, bitcnt_t cnt, bitcnt_t bitlen)277{278u8 owlen = (u8)BIT_LEN_WORDS(bitlen);279int ret;280nn tmp;281tmp.magic = WORD(0);282283MUST_HAVE((bitlen <= NN_MAX_BIT_LEN), ret, err);284MUST_HAVE((cnt < bitlen), ret, err);285286ret = nn_check_initialized(in); EG(ret, err);287ret = nn_init(&tmp, 0); EG(ret, err);288ret = nn_lshift(&tmp, in, (bitcnt_t)(bitlen - cnt)); EG(ret, err);289ret = nn_set_wlen(&tmp, owlen); EG(ret, err);290ret = nn_rshift(out, in, cnt); EG(ret, err);291ret = nn_set_wlen(out, owlen); EG(ret, err);292ret = nn_xor(out, out, &tmp); EG(ret, err);293294/* Mask the last word if necessary */295if (((bitlen % WORD_BITS) != 0) && (out->wlen > 0)) {296/* shift operation below is ok (less than WORD_BITS) */297word_t mask = (word_t)(((word_t)(WORD(1) << (bitlen % WORD_BITS))) - 1);298out->val[out->wlen - 1] &= mask;299}300301err:302nn_uninit(&tmp);303304return ret;305}306307/*308* This function left rotates the input NN value by the value 'cnt' on the309* bitlen basis. The function does it in the following way; Left rotation310* of x by cnt is "simply": (x << cnt) ^ (x >> (bitlen - cnt))311*312* The function returns 0 on success, -1 on error.313*314* Aliasing supported.315*/316int nn_lrot(nn_t out, nn_src_t in, bitcnt_t cnt, bitcnt_t bitlen)317{318u8 owlen = (u8)BIT_LEN_WORDS(bitlen);319int ret;320nn tmp;321tmp.magic = WORD(0);322323MUST_HAVE(!(bitlen > NN_MAX_BIT_LEN), ret, err);324MUST_HAVE(!(cnt >= bitlen), ret, err);325326ret = nn_check_initialized(in); EG(ret, err);327ret = nn_init(&tmp, 0); EG(ret, err);328ret = nn_lshift(&tmp, in, cnt); EG(ret, err);329ret = nn_set_wlen(&tmp, owlen); EG(ret, err);330ret = nn_rshift(out, in, (bitcnt_t)(bitlen - cnt)); EG(ret, err);331ret = nn_set_wlen(out, owlen); EG(ret, err);332ret = nn_xor(out, out, &tmp); EG(ret, err);333334/* Mask the last word if necessary */335if (((bitlen % WORD_BITS) != 0) && (out->wlen > 0)) {336word_t mask = (word_t)(((word_t)(WORD(1) << (bitlen % WORD_BITS))) - 1);337out->val[out->wlen - 1] &= mask;338}339340err:341nn_uninit(&tmp);342343return ret;344}345346/*347* Compute XOR between B and C and put the result in A. B and C must be348* initialized. Aliasing is supported, i.e. A can be one of the parameter B or349* C. If aliasing is not used, A will be initialized by the function. Function350* execution time depends on the word length of larger parameter but not on its351* particular value.352*353* The function returns 0 on success, -1 on error.354*355* Aliasing supported.356*/357int nn_xor(nn_t A, nn_src_t B, nn_src_t C)358{359int ret;360u8 i;361362ret = nn_check_initialized(B); EG(ret, err);363ret = nn_check_initialized(C); EG(ret, err);364365/* Initialize the output if no aliasing is used */366if ((A != B) && (A != C)) {367ret = nn_init(A, 0); EG(ret, err);368}369370/* Set output wlen accordingly */371A->wlen = (C->wlen < B->wlen) ? B->wlen : C->wlen;372373for (i = 0; i < A->wlen; i++) {374A->val[i] = (B->val[i] ^ C->val[i]);375}376377err:378return ret;379}380381/*382* Compute logical OR between B and C and put the result in A. B and C must be383* initialized. Aliasing is supported, i.e. A can be one of the parameter B or384* C. If aliasing is not used, A will be initialized by the function. Function385* execution time depends on the word length of larger parameter but not on its386* particular value.387*388* The function returns 0 on success, -1 on error.389*390* Aliasing supported.391*/392int nn_or(nn_t A, nn_src_t B, nn_src_t C)393{394int ret;395u8 i;396397ret = nn_check_initialized(B); EG(ret, err);398ret = nn_check_initialized(C); EG(ret, err);399400/* Initialize the output if no aliasing is used */401if ((A != B) && (A != C)) {402ret = nn_init(A, 0); EG(ret, err);403}404405/* Set output wlen accordingly */406A->wlen = (C->wlen < B->wlen) ? B->wlen : C->wlen;407408for (i = 0; i < A->wlen; i++) {409A->val[i] = (B->val[i] | C->val[i]);410}411412err:413return ret;414}415416/*417* Compute logical AND between B and C and put the result in A. B and C must be418* initialized. Aliasing is supported, i.e. A can be one of the parameter B or419* C. If aliasing is not used, A will be initialized by the function. Function420* execution time depends on the word length of larger parameter but not on its421* particular value.422*423* The function returns 0 on success, -1 on error.424*425* Aliasing supported.426*/427int nn_and(nn_t A, nn_src_t B, nn_src_t C)428{429int ret;430u8 i;431432ret = nn_check_initialized(B); EG(ret, err);433ret = nn_check_initialized(C); EG(ret, err);434435/* Initialize the output if no aliasing is used */436if ((A != B) && (A != C)) {437ret = nn_init(A, 0); EG(ret, err);438}439440/* Set output wlen accordingly */441A->wlen = (C->wlen < B->wlen) ? B->wlen : C->wlen;442443for (i = 0; i < A->wlen; i++) {444A->val[i] = (B->val[i] & C->val[i]);445}446447err:448return ret;449}450451/*452* Compute logical NOT of B and put the result in A. B must be initialized.453* Aliasing is supported. If aliasing is not used, A will be initialized by454* the function.455*456* The function returns 0 on success, -1 on error.457*458* Aliasing supported.459*/460int nn_not(nn_t A, nn_src_t B)461{462int ret;463u8 i;464465ret = nn_check_initialized(B); EG(ret, err);466467/* Initialize the output if no aliasing is used */468if (A != B) {469ret = nn_init(A, 0); EG(ret, err);470}471472/* Set output wlen accordingly */473A->wlen = B->wlen;474475for (i = 0; i < A->wlen; i++) {476A->val[i] = (word_t)(~(B->val[i]));477}478479err:480return ret;481}482483/* Count leading zeros of a word. This is constant time */484ATTRIBUTE_WARN_UNUSED_RET static u8 wclz(word_t A)485{486u8 cnt = WORD_BITS, over = 0;487int i;488489for (i = (WORD_BITS - 1); i >= 0; i--) {490/* i is less than WORD_BITS so shift operations below are ok */491u8 mask = (u8)(((A & (WORD(1) << i)) >> i) & 0x1);492over |= mask;493cnt = (u8)(cnt - over);494}495496return cnt;497}498499/*500* Count leading zeros of an initialized nn. This is NOT constant time. The501* function returns 0 on success, -1 on error. On success, the number of502* leading zeroes is available in 'lz'. 'lz' is not meaningful on error.503*/504int nn_clz(nn_src_t in, bitcnt_t *lz)505{506bitcnt_t cnt = 0;507int ret;508u8 i;509510/* Sanity check */511MUST_HAVE((lz != NULL), ret, err);512ret = nn_check_initialized(in); EG(ret, err);513514for (i = in->wlen; i > 0; i--) {515if (in->val[i - 1] == 0) {516cnt = (bitcnt_t)(cnt + WORD_BITS);517} else {518cnt = (bitcnt_t)(cnt + wclz(in->val[i - 1]));519break;520}521}522*lz = cnt;523524err:525return ret;526}527528/*529* Compute bit length of given nn. This is NOT constant-time. The530* function returns 0 on success, -1 on error. On success, the bit length531* of 'in' is available in 'blen'. 'blen' is not meaningful on error.532*/533int nn_bitlen(nn_src_t in, bitcnt_t *blen)534{535bitcnt_t _blen = 0;536int ret;537u8 i;538539/* Sanity check */540MUST_HAVE((blen != NULL), ret, err);541ret = nn_check_initialized(in); EG(ret, err);542543for (i = in->wlen; i > 0; i--) {544if (in->val[i - 1] != 0) {545_blen = (bitcnt_t)((i * WORD_BITS) - wclz(in->val[i - 1]));546break;547}548}549(*blen) = _blen;550551err:552return ret;553}554555/*556* On success (return value is 0), the function provides via 'bitval' the value557* of the bit at position 'bit' in 'in' nn. 'bitval' in not meaningful error558* (when return value is -1).559*/560int nn_getbit(nn_src_t in, bitcnt_t bit, u8 *bitval)561{562bitcnt_t widx = bit / WORD_BITS;563u8 bidx = bit % WORD_BITS;564int ret;565566/* Sanity check */567MUST_HAVE((bitval != NULL), ret, err);568ret = nn_check_initialized(in); EG(ret, err);569MUST_HAVE((bit < NN_MAX_BIT_LEN), ret, err);570571/* bidx is less than WORD_BITS so shift operations below are ok */572(*bitval) = (u8)((((in->val[widx]) & (WORD(1) << bidx)) >> bidx) & 0x1);573574err:575return ret;576}577578579