/*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#define NN_CONSISTENCY_CHECK16#include <libecc/nn/nn.h>1718/*19* Used for the conditional swap algorithm SCA20* resistance, see below in the implementation of21* nn_cnd_swap.22*/23#include <libecc/utils/utils_rand.h>2425/*26* Except otherwise specified, all functions accept *initialized* nn.27* The WORD(NN_MAX_WORD_LEN + WORDSIZE) magic is here to detect modules28* compiled with different WORDSIZE or NN_MAX_WORD_LEN and are binary29* incompatible.30*/3132#define NN_MAGIC ((word_t)((0xb4cf5d56e2023316ULL ^ (WORD(NN_MAX_WORD_LEN + WORDSIZE)))))3334/*35* Local helper internally used to check that the storage space36* above wlen is made of zero words. The function does NOT check37* if given nn has been initialized. This must have been done38* by the caller.39*40* Due to its performance cost, this consistency check is used41* in SHOULD_HAVE macros, meaning that it will only be present42* in DEBUG mode. Hence the ATTRIBUTE_UNUSED so that no warning43* (error in -Werror) is triggered at compilation time.44*45*/46ATTRIBUTE_WARN_UNUSED_RET static int ATTRIBUTE_UNUSED __nn_is_wlen_consistent(nn_src_t A)47{48word_t val = 0;49u8 i;5051for (i = A->wlen; i < NN_MAX_WORD_LEN; i++) {52val |= (A)->val[i];53}5455return (val == 0);56}5758/*59* Verify that pointed nn has already been initialized. This function60* should be used as a safety net in all function before using a nn61* received as parameter. Returns 0 on success, -1 on error.62*/63int nn_check_initialized(nn_src_t A)64{65int ret;6667MUST_HAVE((A != NULL), ret, err);68MUST_HAVE((A->magic == NN_MAGIC), ret, err);69MUST_HAVE((A->wlen <= NN_MAX_WORD_LEN), ret, err);70SHOULD_HAVE(__nn_is_wlen_consistent(A), ret, err);7172ret = 0;7374err:75return ret;76}7778/*79* Initialize nn from expected initial byte length 'len', setting its wlen80* to associated (ceil) value and clearing whole storage space. Return 081* on success, -1 on error.82*/83int nn_init(nn_t A, u16 len)84{85int ret;86u8 i;8788MUST_HAVE(((A != NULL) && (len <= NN_MAX_BYTE_LEN)), ret, err);8990A->wlen = (u8)BYTE_LEN_WORDS(len);91A->magic = NN_MAGIC;9293for (i = 0; i < NN_MAX_WORD_LEN; i++) {94A->val[i] = WORD(0);95}9697ret = 0;9899err:100return ret;101}102103/*104* Uninitialize the pointed nn to prevent further use (magic field in the105* structure is zeroized). The associated storage space is also zeroized. If106* given pointer is NULL or does not point to an initialized nn, the function107* does nothing.108*/109void nn_uninit(nn_t A)110{111if ((A != NULL) && (A->magic == NN_MAGIC)) {112int i;113114for (i = 0; i < NN_MAX_WORD_LEN; i++) {115A->val[i] = WORD(0);116}117118A->wlen = 0;119A->magic = WORD(0);120}121122return;123}124125/*126* Set current value of pointed initialized nn to 0. Returns 0 on success, -1127* on error.128*/129int nn_zero(nn_t A)130{131int ret;132133ret = nn_check_initialized(A); EG(ret, err);134ret = nn_init(A, 0);135136err:137return ret;138}139140/*141* Set current value of pointed initialized nn to given word value. Returns 0142* on success, -1 on error.143*/144int nn_set_word_value(nn_t A, word_t val)145{146int ret;147148ret = nn_zero(A); EG(ret, err);149150A->val[0] = val;151A->wlen = 1;152153err:154return ret;155}156157/*158* Set current value of pointed initialized nn to 1. Returns 0 on success, -1159* on error.160*/161int nn_one(nn_t A)162{163return nn_set_word_value(A, WORD(1));164}165166/*167* Conditionally swap two nn's content *in constant time*. Swapping is done168* if 'cnd' is not zero. Nothing is done otherwise. Returns 0 on success, -1169* on error.170*171* Aliasing of inputs is supported.172*/173int nn_cnd_swap(int cnd, nn_t in1, nn_t in2)174{175word_t mask = WORD_MASK_IFNOTZERO(cnd);176u8 len, i;177word_t t, r;178volatile word_t r_mask;179int ret;180181ret = nn_check_initialized(in1); EG(ret, err);182ret = nn_check_initialized(in2); EG(ret, err);183184MUST_HAVE((in1->wlen <= NN_MAX_WORD_LEN), ret, err);185MUST_HAVE((in2->wlen <= NN_MAX_WORD_LEN), ret, err);186187len = (in1->wlen >= in2->wlen) ? in1->wlen : in2->wlen;188189/* Use a random word for randomly masking the delta value hamming190* weight as proposed in Algorithm 4 of "Nonce@once: A Single-Trace191* EM Side Channel Attack on Several Constant-Time Elliptic192* Curve Implementations in Mobile Platforms" by Alam et al.193*/194ret = get_unsafe_random((u8*)&r, sizeof(r)); EG(ret, err);195r_mask = r;196197for (i = 0; i < NN_MAX_WORD_LEN; i++) {198word_t local_mask = WORD_MASK_IFNOTZERO((i < len));199t = ((in1->val[i] ^ in2->val[i]) & mask) ^ r_mask;200in1->val[i] ^= ((t & local_mask) ^ (r_mask & local_mask));201in2->val[i] ^= ((t & local_mask) ^ (r_mask & local_mask));202}203204t = (word_t)(((in1->wlen ^ in2->wlen) & mask) ^ r_mask);205in1->wlen ^= (u8)(t ^ r_mask);206in2->wlen ^= (u8)(t ^ r_mask);207208err:209return ret;210}211212/*213* Adjust internal wlen attribute of given nn to new_wlen. If internal wlen214* attribute value is reduced, words above that limit in A are zeroized.215* new_wlen must be in [0, NN_MAX_WORD_LEN].216* The trimming is performed in constant time wrt to the length of the217* input to avoid leaking it.218* Returns 0 on success, -1 on error.219*/220int nn_set_wlen(nn_t A, u8 new_wlen)221{222int ret;223u8 i;224225ret = nn_check_initialized(A); EG(ret, err);226MUST_HAVE((new_wlen <= NN_MAX_WORD_LEN), ret, err);227MUST_HAVE((A->wlen <= NN_MAX_WORD_LEN), ret, err);228229/* Trimming performed in constant time */230for (i = 0; i < NN_MAX_WORD_LEN; i++) {231A->val[i] = (word_t)(A->val[i] & WORD_MASK_IFZERO((i >= new_wlen)));232}233234A->wlen = new_wlen;235236err:237return ret;238}239240/*241* The function tests if given nn value is zero. The result of the test is given242* using 'iszero' out parameter (1 if nn is zero, 0 if it is not). The function243* returns 0 on success, -1 on error. 'iszero' is not meaningfull on error.244* When A is valid, check is done *in constant time*.245*/246int nn_iszero(nn_src_t A, int *iszero)247{248int ret, notzero;249u8 i;250251ret = nn_check_initialized(A); EG(ret, err);252MUST_HAVE((A->wlen <= NN_MAX_WORD_LEN), ret, err);253MUST_HAVE((iszero != NULL), ret, err);254255notzero = 0;256for (i = 0; i < NN_MAX_WORD_LEN; i++) {257int mask = ((i < A->wlen) ? 1 : 0);258notzero |= ((A->val[i] != 0) & mask);259}260261*iszero = !notzero;262263err:264return ret;265}266267/*268* The function tests if given nn value is one. The result of the test is given269* using 'isone' out parameter (1 if nn is one, 0 if it is not). The function270* returns 0 on success, -1 on error. 'isone' is not meaningfull on error.271* When A is valid, check is done *in constant time*.272*/273int nn_isone(nn_src_t A, int *isone)274{275int ret, notone;276u8 i;277278ret = nn_check_initialized(A); EG(ret, err);279MUST_HAVE(!(A->wlen > NN_MAX_WORD_LEN), ret, err);280MUST_HAVE((isone != NULL), ret, err);281282/* val[0] access is ok no matter wlen value */283notone = (A->val[0] != 1);284for (i = 1; i < NN_MAX_WORD_LEN; i++) {285int mask = ((i < A->wlen) ? 1 : 0);286notone |= ((A->val[i] != 0) & mask);287}288289*isone = !notone;290291err:292return ret;293}294295/*296* The function tests if given nn value is odd. The result of the test is given297* using 'isodd' out parameter (1 if nn is odd, 0 if it is not). The function298* returns 0 on success, -1 on error. 'isodd' is not meaningfull on error.299*/300int nn_isodd(nn_src_t A, int *isodd)301{302int ret;303304ret = nn_check_initialized(A); EG(ret, err);305MUST_HAVE((isodd != NULL), ret, err);306307*isodd = (A->wlen != 0) && (A->val[0] & 1);308309err:310return ret;311}312313/*314* Compare given nn against given word value. This is done *in constant time*315* (only depending on the input length, not on its value or on the word value)316* when provided nn is valid. The function returns 0 on success and provides317* the comparison value in 'cmp' parameter. -1 is returned on error, in which318* case 'cmp' is not meaningful.319*/320int nn_cmp_word(nn_src_t in, word_t w, int *cmp)321{322int ret, tmp = 0;323word_t mask;324u8 i;325326ret = nn_check_initialized(in); EG(ret, err);327MUST_HAVE((cmp != NULL), ret, err);328329/* No need to read, we can conclude */330if (in->wlen == 0) {331*cmp = -(w != 0);332ret = 0;333goto err;334}335336/*337* Let's loop on all words above first one to see if one338* of those is non-zero.339*/340for (i = (u8)(in->wlen - 1); i > 0; i--) {341tmp |= (in->val[i] != 0);342}343344/*345* Compare first word of nn w/ w if needed. This346* is done w/ masking to avoid doing or not doing347* it based on 'tmp' (i.e. fact that a high word348* of nn is not zero).349*/350mask = WORD_MASK_IFZERO(tmp);351tmp += (int)(((word_t)(in->val[i] > w)) & (mask));352tmp -= (int)(((word_t)(in->val[i] < w)) & (mask));353*cmp = tmp;354355err:356return ret;357}358359/*360* Compare given two nn 'A' and '. This is done *in constant time* (only361* depending on the largest length of the inputs, not on their values). The362* function returns 0 on success and provides the comparison value in363* 'cmp' parameter (0 if A == B, -1 if A < B, +1 if A > B). -1 is returned364* on error, in which case 'cmp' is not meaningful.365*366* Aliasing of inputs is supported.367*/368int nn_cmp(nn_src_t A, nn_src_t B, int *cmp)369{370int tmp, mask, ret, i;371u8 cmp_len;372373ret = nn_check_initialized(A); EG(ret, err);374ret = nn_check_initialized(B); EG(ret, err);375MUST_HAVE((cmp != NULL), ret, err);376377cmp_len = (A->wlen >= B->wlen) ? A->wlen : B->wlen;378379tmp = 0;380for (i = (cmp_len - 1); i >= 0; i--) { /* ok even if cmp_len is 0 */381mask = !(tmp & 0x1);382tmp += ((A->val[i] > B->val[i]) & mask);383tmp -= ((A->val[i] < B->val[i]) & mask);384}385(*cmp) = tmp;386387err:388return ret;389}390391/*392* Copy given nn 'src_nn' value into 'dst_nn'. This is done *in constant time*.393* 'dst_nn' must point to a declared nn, but *need not be initialized*; it will394* be (manually) initialized by the function. 'src_nn' must have been395* initialized prior to the call. The function returns 0 on success, -1 on error.396*397* Alising of input and output is supported.398*/399int nn_copy(nn_t dst_nn, nn_src_t src_nn)400{401int ret;402u8 i;403404MUST_HAVE((dst_nn != NULL), ret, err);405ret = nn_check_initialized(src_nn); EG(ret, err);406407for (i = 0; i < NN_MAX_WORD_LEN; i++) {408dst_nn->val[i] = src_nn->val[i];409}410411dst_nn->wlen = src_nn->wlen;412dst_nn->magic = NN_MAGIC;413414err:415return ret;416}417418/*419* Update wlen value of given nn if a set of words below wlen value are zero.420* The function is *not constant time*, i.e. it depends on the input value.421* The function returns 0 on sucess, -1 on error.422*/423int nn_normalize(nn_t in1)424{425int ret;426427ret = nn_check_initialized(in1); EG(ret, err);428429while ((in1->wlen > 0) && (in1->val[in1->wlen - 1] == 0)) {430in1->wlen--;431}432433err:434return ret;435}436437/*438* Convert given consecutive WORD_BYTES bytes pointed by 'val' from network (big439* endian) order to host order. 'val' needs not point to a word-aligned region.440* The function returns 0 on success, -1 on error. On success, the result is441* provided in 'out'. 'out' is not meaningful on error.442*/443ATTRIBUTE_WARN_UNUSED_RET static int _ntohw(const u8 *val, word_t *out)444{445word_t res = 0;446u8 *res_buf = (u8 *)(&res);447int i, ret;448449MUST_HAVE(((val != NULL) && (out != NULL)), ret, err);450451if (arch_is_big_endian()) {452/* copy bytes, one by one to avoid alignement issues */453for (i = 0; i < WORD_BYTES; i++) {454res_buf[i] = val[i];455}456} else {457u8 tmp;458459for (i = 0; i < (WORD_BYTES / 2); i++) {460tmp = val[i];461res_buf[i] = val[WORD_BYTES - i - 1];462res_buf[WORD_BYTES - i - 1] = tmp;463}464465VAR_ZEROIFY(tmp);466}467468*out = res;469ret = 0;470471err:472return ret;473}474475/* Same as previous function but from host to network byte order. */476ATTRIBUTE_WARN_UNUSED_RET static inline int _htonw(const u8 *val, word_t *out)477{478return _ntohw(val, out);479}480481/*482* 'out_nn' is expected to point to the storage location of a declared nn,483* which will be initialized by the function (i.e. given nn need not be484* initialized). The function then imports value (expected to be in big485* endian) from given buffer 'buf' of length 'buflen' into it. The function486* expects (and enforces) that buflen is less than or equal to NN_MAX_BYTE_LEN.487* The function returns 0 on success, -1 on error.488*/489int nn_init_from_buf(nn_t out_nn, const u8 *buf, u16 buflen)490{491u8 tmp[NN_MAX_BYTE_LEN];492u16 wpos;493int ret;494495MUST_HAVE(((out_nn != NULL) && (buf != NULL) &&496(buflen <= NN_MAX_BYTE_LEN)), ret, err);497498ret = local_memset(tmp, 0, (u32)(NN_MAX_BYTE_LEN - buflen)); EG(ret, err);499ret = local_memcpy(tmp + NN_MAX_BYTE_LEN - buflen, buf, buflen); EG(ret, err);500501ret = nn_init(out_nn, buflen); EG(ret, err);502503for (wpos = 0; wpos < NN_MAX_WORD_LEN; wpos++) {504u16 buf_pos = (u16)((NN_MAX_WORD_LEN - wpos - 1) * WORD_BYTES);505ret = _ntohw(tmp + buf_pos, &(out_nn->val[wpos])); EG(ret, err);506}507508ret = local_memset(tmp, 0, NN_MAX_BYTE_LEN);509510err:511return ret;512}513514/*515* Export 'buflen' LSB bytes of given nn as a big endian buffer. If buffer516* length is larger than effective size of input nn, padding w/ zero is517* performed. If buffer size is smaller than input nn effective size,518* MSB bytes are simply lost in exported buffer. The function returns 0519* on success, -1 on error.520*/521int nn_export_to_buf(u8 *buf, u16 buflen, nn_src_t in_nn)522{523u8 *src_word_ptr, *dst_word_ptr;524const u8 wb = WORD_BYTES;525u16 remain = buflen;526int ret;527u8 i;528529MUST_HAVE((buf != NULL), ret, err);530ret = nn_check_initialized(in_nn); EG(ret, err);531532ret = local_memset(buf, 0, buflen); EG(ret, err);533534/*535* We consider each word in input nn one at a time and convert536* it to big endian in a temporary word. Based on remaining537* length of output buffer, we copy the LSB bytes of temporary538* word into it at current position. That way, filling of the539* buffer is performed from its end to its beginning, word by540* word, except for the last one, which may be shorten if541* given buffer length is not a multiple of word length.542*/543for (i = 0; remain && (i < in_nn->wlen); i++) {544u16 copylen = (remain > wb) ? wb : remain;545word_t val;546547ret = _htonw((const u8 *)&in_nn->val[i], &val); EG(ret, err);548549dst_word_ptr = (buf + buflen - (i * wb) - copylen);550src_word_ptr = (u8 *)(&val) + wb - copylen;551552ret = local_memcpy(dst_word_ptr, src_word_ptr, copylen); EG(ret, err);553src_word_ptr = NULL;554555remain = (u16)(remain - copylen);556}557558err:559return ret;560}561562/*563* Given a table 'tab' pointing to a set of 'tabsize' NN elements, the564* function copies the value of element at position idx (idx < tabsize)565* in 'out' parameters. Masking is used to avoid leaking which element566* was copied.567*568* Note that the main copying loop is done on the maximum bits for all569* NN elements and not based on the specific effective size of each570* NN elements in 'tab'571*572* Returns 0 on success, -1 on error.573*574* Aliasing of out and the selected element inside the tab is NOT supported.575*/576int nn_tabselect(nn_t out, u8 idx, nn_src_t *tab, u8 tabsize)577{578u8 i, k;579word_t mask;580int ret;581582/* Basic sanity checks */583MUST_HAVE(((tab != NULL) && (idx < tabsize)), ret, err);584585ret = nn_check_initialized(out); EG(ret, err);586587/* Zeroize out and enforce its size. */588ret = nn_zero(out); EG(ret, err);589590out->wlen = 0;591592for (k = 0; k < tabsize; k++) {593/* Check current element is initialized */594ret = nn_check_initialized(tab[k]); EG(ret, err);595596mask = WORD_MASK_IFNOTZERO(idx == k);597598out->wlen = (u8)(out->wlen | ((tab[k]->wlen) & mask));599600for (i = 0; i < NN_MAX_WORD_LEN; i++) {601out->val[i] |= (tab[k]->val[i] & mask);602}603}604605err:606return ret;607}608609610