1*f0865ec9SKyle Evans /* 2*f0865ec9SKyle Evans * Copyright (C) 2021 - This file is part of libecc project 3*f0865ec9SKyle Evans * 4*f0865ec9SKyle Evans * Authors: 5*f0865ec9SKyle Evans * Ryad BENADJILA <ryadbenadjila@gmail.com> 6*f0865ec9SKyle Evans * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr> 7*f0865ec9SKyle Evans * Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr> 8*f0865ec9SKyle Evans * 9*f0865ec9SKyle Evans * Contributors: 10*f0865ec9SKyle Evans * Nicolas VIVET <nicolas.vivet@ssi.gouv.fr> 11*f0865ec9SKyle Evans * Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr> 12*f0865ec9SKyle Evans * 13*f0865ec9SKyle Evans * This software is licensed under a dual BSD and GPL v2 license. 14*f0865ec9SKyle Evans * See LICENSE file at the root folder of the project. 15*f0865ec9SKyle Evans */ 16*f0865ec9SKyle Evans #include <libecc/nn/nn_mul_redc1.h> 17*f0865ec9SKyle Evans #include <libecc/nn/nn_div_public.h> 18*f0865ec9SKyle Evans #include <libecc/nn/nn_logical.h> 19*f0865ec9SKyle Evans #include <libecc/nn/nn_mod_pow.h> 20*f0865ec9SKyle Evans #include <libecc/nn/nn_rand.h> 21*f0865ec9SKyle Evans #include <libecc/nn/nn.h> 22*f0865ec9SKyle Evans 23*f0865ec9SKyle Evans /* 24*f0865ec9SKyle Evans * NOT constant time with regard to the bitlength of exp. 25*f0865ec9SKyle Evans * 26*f0865ec9SKyle Evans * Implements a left to right Montgomery Ladder for modular exponentiation. 27*f0865ec9SKyle Evans * This is an internal common helper and assumes that all the initialization 28*f0865ec9SKyle Evans * and aliasing of inputs/outputs are handled by the callers. Depending on 29*f0865ec9SKyle Evans * the inputs, redcification is optionally used. 30*f0865ec9SKyle Evans * The base is reduced if necessary. 31*f0865ec9SKyle Evans * 32*f0865ec9SKyle Evans * Montgomery Ladder is masked using Itoh et al. anti-ADPA 33*f0865ec9SKyle Evans * (Address-bit DPA) countermeasure. 34*f0865ec9SKyle Evans * See "A Practical Countermeasure against Address-Bit Differential Power Analysis" 35*f0865ec9SKyle Evans * by Itoh, Izu and Takenaka for more information. 36*f0865ec9SKyle Evans 37*f0865ec9SKyle Evans * Returns 0 on success, -1 on error. 38*f0865ec9SKyle Evans */ 39*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_exp_monty_ladder_ltr(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv) 40*f0865ec9SKyle Evans { 41*f0865ec9SKyle Evans nn T[3]; 42*f0865ec9SKyle Evans nn mask; 43*f0865ec9SKyle Evans bitcnt_t explen, oldexplen; 44*f0865ec9SKyle Evans u8 expbit, rbit; 45*f0865ec9SKyle Evans int ret, cmp; 46*f0865ec9SKyle Evans T[0].magic = T[1].magic = T[2].magic = mask.magic = WORD(0); 47*f0865ec9SKyle Evans 48*f0865ec9SKyle Evans /* Initialize out */ 49*f0865ec9SKyle Evans ret = nn_init(out, 0); EG(ret, err); 50*f0865ec9SKyle Evans 51*f0865ec9SKyle Evans ret = nn_init(&T[0], 0); EG(ret, err); 52*f0865ec9SKyle Evans ret = nn_init(&T[1], 0); EG(ret, err); 53*f0865ec9SKyle Evans ret = nn_init(&T[2], 0); EG(ret, err); 54*f0865ec9SKyle Evans 55*f0865ec9SKyle Evans /* Generate our Itoh random mask */ 56*f0865ec9SKyle Evans ret = nn_get_random_len(&mask, NN_MAX_BYTE_LEN); EG(ret, err); 57*f0865ec9SKyle Evans 58*f0865ec9SKyle Evans ret = nn_bitlen(exp, &explen); EG(ret, err); 59*f0865ec9SKyle Evans 60*f0865ec9SKyle Evans 61*f0865ec9SKyle Evans /* From now on, since we deal with Itoh's countermeasure, we must have at 62*f0865ec9SKyle Evans * least 2 bits in the exponent. We will deal with the particular cases of 0 and 1 63*f0865ec9SKyle Evans * bit exponents later. 64*f0865ec9SKyle Evans */ 65*f0865ec9SKyle Evans oldexplen = explen; 66*f0865ec9SKyle Evans explen = (explen < 2) ? 2 : explen; 67*f0865ec9SKyle Evans 68*f0865ec9SKyle Evans ret = nn_getbit(&mask, (bitcnt_t)(explen - 1), &rbit); EG(ret, err); 69*f0865ec9SKyle Evans 70*f0865ec9SKyle Evans /* Reduce the base if necessary */ 71*f0865ec9SKyle Evans ret = nn_cmp(base, mod, &cmp); EG(ret, err); 72*f0865ec9SKyle Evans if(cmp >= 0){ 73*f0865ec9SKyle Evans /* Modular reduction */ 74*f0865ec9SKyle Evans ret = nn_mod(&T[rbit], base, mod); EG(ret, err); 75*f0865ec9SKyle Evans if(r != NULL){ 76*f0865ec9SKyle Evans /* Redcify the base if necessary */ 77*f0865ec9SKyle Evans ret = nn_mul_redc1(&T[rbit], &T[rbit], r_square, mod, mpinv); EG(ret, err); 78*f0865ec9SKyle Evans } 79*f0865ec9SKyle Evans } 80*f0865ec9SKyle Evans else{ 81*f0865ec9SKyle Evans if(r != NULL){ 82*f0865ec9SKyle Evans /* Redcify the base if necessary */ 83*f0865ec9SKyle Evans ret = nn_mul_redc1(&T[rbit], base, r_square, mod, mpinv); EG(ret, err); 84*f0865ec9SKyle Evans } 85*f0865ec9SKyle Evans else{ 86*f0865ec9SKyle Evans ret = nn_copy(&T[rbit], base); EG(ret, err); 87*f0865ec9SKyle Evans } 88*f0865ec9SKyle Evans } 89*f0865ec9SKyle Evans 90*f0865ec9SKyle Evans /* We implement the Montgomery ladder exponentiation with Itoh masking using three 91*f0865ec9SKyle Evans * registers T[0], T[1] and T[2]. The random mask is in 'mask'. 92*f0865ec9SKyle Evans */ 93*f0865ec9SKyle Evans if(r != NULL){ 94*f0865ec9SKyle Evans ret = nn_mul_redc1(&T[1-rbit], &T[rbit], &T[rbit], mod, mpinv); EG(ret, err); 95*f0865ec9SKyle Evans } 96*f0865ec9SKyle Evans else{ 97*f0865ec9SKyle Evans ret = nn_mod_mul(&T[1-rbit], &T[rbit], &T[rbit], mod); EG(ret, err); 98*f0865ec9SKyle Evans } 99*f0865ec9SKyle Evans 100*f0865ec9SKyle Evans /* Now proceed with the Montgomery Ladder algorithm. 101*f0865ec9SKyle Evans */ 102*f0865ec9SKyle Evans explen = (bitcnt_t)(explen - 1); 103*f0865ec9SKyle Evans while (explen > 0) { 104*f0865ec9SKyle Evans u8 rbit_next; 105*f0865ec9SKyle Evans explen = (bitcnt_t)(explen - 1); 106*f0865ec9SKyle Evans 107*f0865ec9SKyle Evans /* rbit is r[i+1], and rbit_next is r[i] */ 108*f0865ec9SKyle Evans ret = nn_getbit(&mask, explen, &rbit_next); EG(ret, err); 109*f0865ec9SKyle Evans /* Get the exponent bit */ 110*f0865ec9SKyle Evans ret = nn_getbit(exp, explen, &expbit); EG(ret, err); 111*f0865ec9SKyle Evans 112*f0865ec9SKyle Evans /* Square */ 113*f0865ec9SKyle Evans if(r != NULL){ 114*f0865ec9SKyle Evans ret = nn_mul_redc1(&T[2], &T[expbit ^ rbit], &T[expbit ^ rbit], mod, mpinv); EG(ret, err); 115*f0865ec9SKyle Evans } 116*f0865ec9SKyle Evans else{ 117*f0865ec9SKyle Evans ret = nn_mod_mul(&T[2], &T[expbit ^ rbit], &T[expbit ^ rbit], mod); EG(ret, err); 118*f0865ec9SKyle Evans } 119*f0865ec9SKyle Evans /* Multiply */ 120*f0865ec9SKyle Evans if(r != NULL){ 121*f0865ec9SKyle Evans ret = nn_mul_redc1(&T[1], &T[0], &T[1], mod, mpinv); EG(ret, err); 122*f0865ec9SKyle Evans } 123*f0865ec9SKyle Evans else{ 124*f0865ec9SKyle Evans ret = nn_mod_mul(&T[1], &T[0], &T[1], mod); EG(ret, err); 125*f0865ec9SKyle Evans } 126*f0865ec9SKyle Evans /* Copy */ 127*f0865ec9SKyle Evans ret = nn_copy(&T[0], &T[2 - (expbit ^ rbit_next)]); EG(ret, err); 128*f0865ec9SKyle Evans ret = nn_copy(&T[1], &T[1 + (expbit ^ rbit_next)]); EG(ret, err); 129*f0865ec9SKyle Evans /* Update rbit */ 130*f0865ec9SKyle Evans rbit = rbit_next; 131*f0865ec9SKyle Evans } 132*f0865ec9SKyle Evans ret = nn_one(&T[1 - rbit]); 133*f0865ec9SKyle Evans if(r != NULL){ 134*f0865ec9SKyle Evans /* Unredcify in out */ 135*f0865ec9SKyle Evans ret = nn_mul_redc1(&T[rbit], &T[rbit], &T[1 - rbit], mod, mpinv); EG(ret, err); 136*f0865ec9SKyle Evans } 137*f0865ec9SKyle Evans 138*f0865ec9SKyle Evans /* Deal with the particular cases of 0 and 1 bit exponents */ 139*f0865ec9SKyle Evans /* Case with 0 bit exponent: T[1 - rbit] contains 1 modulo mod */ 140*f0865ec9SKyle Evans ret = nn_mod(&T[1 - rbit], &T[1 - rbit], mod); EG(ret, err); 141*f0865ec9SKyle Evans /* Case with 1 bit exponent */ 142*f0865ec9SKyle Evans ret = nn_mod(&T[2], base, mod); EG(ret, err); 143*f0865ec9SKyle Evans /* Proceed with the output */ 144*f0865ec9SKyle Evans ret = nn_cnd_swap((oldexplen == 0), out, &T[1 - rbit]); 145*f0865ec9SKyle Evans ret = nn_cnd_swap((oldexplen == 1), out, &T[2]); 146*f0865ec9SKyle Evans ret = nn_cnd_swap(((oldexplen != 0) && (oldexplen != 1)), out, &T[rbit]); 147*f0865ec9SKyle Evans 148*f0865ec9SKyle Evans err: 149*f0865ec9SKyle Evans nn_uninit(&T[0]); 150*f0865ec9SKyle Evans nn_uninit(&T[1]); 151*f0865ec9SKyle Evans nn_uninit(&T[2]); 152*f0865ec9SKyle Evans nn_uninit(&mask); 153*f0865ec9SKyle Evans 154*f0865ec9SKyle Evans return ret; 155*f0865ec9SKyle Evans } 156*f0865ec9SKyle Evans 157*f0865ec9SKyle Evans /* 158*f0865ec9SKyle Evans * NOT constant time with regard to the bitlength of exp. 159*f0865ec9SKyle Evans * 160*f0865ec9SKyle Evans * Reduces the base modulo mod if it is not already reduced, 161*f0865ec9SKyle Evans * which is also a small divergence wrt constant time leaking 162*f0865ec9SKyle Evans * the information that base <= mod or not: please use with care 163*f0865ec9SKyle Evans * in the callers if this information is sensitive. 164*f0865ec9SKyle Evans * 165*f0865ec9SKyle Evans * Aliasing not supported. Expects caller to check parameters 166*f0865ec9SKyle Evans * have been initialized. This is an internal helper. 167*f0865ec9SKyle Evans * 168*f0865ec9SKyle Evans * Compute (base ** exp) mod (mod) using a Montgomery Ladder algorithm 169*f0865ec9SKyle Evans * with Montgomery redcification, hence the Montgomery coefficients as input. 170*f0865ec9SKyle Evans * The module "mod" is expected to be odd for redcification to be used. 171*f0865ec9SKyle Evans * 172*f0865ec9SKyle Evans * Returns 0 on success, -1 on error. 173*f0865ec9SKyle Evans */ 174*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_mod_pow_redc(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv) 175*f0865ec9SKyle Evans { 176*f0865ec9SKyle Evans return _nn_exp_monty_ladder_ltr(out, base, exp, mod, r, r_square, mpinv); 177*f0865ec9SKyle Evans } 178*f0865ec9SKyle Evans 179*f0865ec9SKyle Evans /* 180*f0865ec9SKyle Evans * NOT constant time with regard to the bitlength of exp. 181*f0865ec9SKyle Evans * 182*f0865ec9SKyle Evans * Reduces the base modulo mod if it is not already reduced, 183*f0865ec9SKyle Evans * which is also a small divergence wrt constant time leaking 184*f0865ec9SKyle Evans * the information that base <= mod or not: please use with care 185*f0865ec9SKyle Evans * in the callers if this information is sensitive. 186*f0865ec9SKyle Evans * 187*f0865ec9SKyle Evans * Aliasing is supported. Expects caller to check parameters 188*f0865ec9SKyle Evans * have been initialized. This is an internal helper. 189*f0865ec9SKyle Evans * 190*f0865ec9SKyle Evans * Compute (base ** exp) mod (mod) using a Montgomery Ladder algorithm. 191*f0865ec9SKyle Evans * This function works for all values of "mod", but is slower that the one 192*f0865ec9SKyle Evans * using Montgomery multiplication (which only works for odd "mod"). Hence, 193*f0865ec9SKyle Evans * it is only used on even "mod" by upper layers. 194*f0865ec9SKyle Evans * 195*f0865ec9SKyle Evans * Returns 0 on success, -1 on error. 196*f0865ec9SKyle Evans */ 197*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_mod_pow(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod) 198*f0865ec9SKyle Evans { 199*f0865ec9SKyle Evans int ret; 200*f0865ec9SKyle Evans 201*f0865ec9SKyle Evans if ((out == base) || (out == exp) || (out == mod)) { 202*f0865ec9SKyle Evans nn _out; 203*f0865ec9SKyle Evans _out.magic = WORD(0); 204*f0865ec9SKyle Evans 205*f0865ec9SKyle Evans ret = nn_init(&_out, 0); EG(ret, err); 206*f0865ec9SKyle Evans ret = _nn_exp_monty_ladder_ltr(&_out, base, exp, mod, NULL, NULL, WORD(0)); EG(ret, err); 207*f0865ec9SKyle Evans ret = nn_copy(out, &_out); 208*f0865ec9SKyle Evans } 209*f0865ec9SKyle Evans else{ 210*f0865ec9SKyle Evans ret = _nn_exp_monty_ladder_ltr(out, base, exp, mod, NULL, NULL, WORD(0)); 211*f0865ec9SKyle Evans } 212*f0865ec9SKyle Evans 213*f0865ec9SKyle Evans err: 214*f0865ec9SKyle Evans return ret; 215*f0865ec9SKyle Evans } 216*f0865ec9SKyle Evans 217*f0865ec9SKyle Evans /* 218*f0865ec9SKyle Evans * Same purpose as above but handles aliasing. 219*f0865ec9SKyle Evans * Expects caller to check parameters 220*f0865ec9SKyle Evans * have been initialized. This is an internal helper. 221*f0865ec9SKyle Evans */ 222*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_mod_pow_redc_aliased(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv) 223*f0865ec9SKyle Evans { 224*f0865ec9SKyle Evans nn _out; 225*f0865ec9SKyle Evans int ret; 226*f0865ec9SKyle Evans _out.magic = WORD(0); 227*f0865ec9SKyle Evans 228*f0865ec9SKyle Evans ret = nn_init(&_out, 0); EG(ret, err); 229*f0865ec9SKyle Evans ret = _nn_mod_pow_redc(&_out, base, exp, mod, r, r_square, mpinv); EG(ret, err); 230*f0865ec9SKyle Evans ret = nn_copy(out, &_out); 231*f0865ec9SKyle Evans 232*f0865ec9SKyle Evans err: 233*f0865ec9SKyle Evans nn_uninit(&_out); 234*f0865ec9SKyle Evans 235*f0865ec9SKyle Evans return ret; 236*f0865ec9SKyle Evans } 237*f0865ec9SKyle Evans 238*f0865ec9SKyle Evans /* Aliased version of previous one. 239*f0865ec9SKyle Evans * NOTE: our nn_mod_pow_redc primitives suppose that the modulo is odd for Montgomery multiplication 240*f0865ec9SKyle Evans * primitives to provide consistent results. 241*f0865ec9SKyle Evans * 242*f0865ec9SKyle Evans * Aliasing is supported. 243*f0865ec9SKyle Evans */ 244*f0865ec9SKyle Evans int nn_mod_pow_redc(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv) 245*f0865ec9SKyle Evans { 246*f0865ec9SKyle Evans int ret, isodd; 247*f0865ec9SKyle Evans 248*f0865ec9SKyle Evans ret = nn_check_initialized(base); EG(ret, err); 249*f0865ec9SKyle Evans ret = nn_check_initialized(exp); EG(ret, err); 250*f0865ec9SKyle Evans ret = nn_check_initialized(mod); EG(ret, err); 251*f0865ec9SKyle Evans ret = nn_check_initialized(r); EG(ret, err); 252*f0865ec9SKyle Evans ret = nn_check_initialized(r_square); EG(ret, err); 253*f0865ec9SKyle Evans 254*f0865ec9SKyle Evans /* Check that we have an odd number for our modulo */ 255*f0865ec9SKyle Evans ret = nn_isodd(mod, &isodd); EG(ret, err); 256*f0865ec9SKyle Evans MUST_HAVE(isodd, ret, err); 257*f0865ec9SKyle Evans 258*f0865ec9SKyle Evans /* Handle the case where our prime is less than two words size. 259*f0865ec9SKyle Evans * We need it to be >= 2 words size for the Montgomery multiplication to be 260*f0865ec9SKyle Evans * usable. 261*f0865ec9SKyle Evans */ 262*f0865ec9SKyle Evans if(mod->wlen < 2){ 263*f0865ec9SKyle Evans /* Local copy our modulo */ 264*f0865ec9SKyle Evans nn _mod; 265*f0865ec9SKyle Evans _mod.magic = WORD(0); 266*f0865ec9SKyle Evans 267*f0865ec9SKyle Evans /* And set its length accordingly */ 268*f0865ec9SKyle Evans ret = nn_copy(&_mod, mod); EG(ret, err1); 269*f0865ec9SKyle Evans ret = nn_set_wlen(&_mod, 2); EG(ret, err1); 270*f0865ec9SKyle Evans /* Handle output aliasing */ 271*f0865ec9SKyle Evans if ((out == base) || (out == exp) || (out == mod) || (out == r) || (out == r_square)) { 272*f0865ec9SKyle Evans ret = _nn_mod_pow_redc_aliased(out, base, exp, &_mod, r, r_square, mpinv); EG(ret, err1); 273*f0865ec9SKyle Evans } else { 274*f0865ec9SKyle Evans ret = _nn_mod_pow_redc(out, base, exp, &_mod, r, r_square, mpinv); EG(ret, err1); 275*f0865ec9SKyle Evans } 276*f0865ec9SKyle Evans err1: 277*f0865ec9SKyle Evans nn_uninit(&_mod); 278*f0865ec9SKyle Evans EG(ret, err); 279*f0865ec9SKyle Evans } 280*f0865ec9SKyle Evans else{ 281*f0865ec9SKyle Evans /* Handle output aliasing */ 282*f0865ec9SKyle Evans if ((out == base) || (out == exp) || (out == mod) || (out == r) || (out == r_square)) { 283*f0865ec9SKyle Evans ret = _nn_mod_pow_redc_aliased(out, base, exp, mod, r, r_square, mpinv); 284*f0865ec9SKyle Evans } else { 285*f0865ec9SKyle Evans ret = _nn_mod_pow_redc(out, base, exp, mod, r, r_square, mpinv); 286*f0865ec9SKyle Evans } 287*f0865ec9SKyle Evans } 288*f0865ec9SKyle Evans 289*f0865ec9SKyle Evans err: 290*f0865ec9SKyle Evans return ret; 291*f0865ec9SKyle Evans } 292*f0865ec9SKyle Evans 293*f0865ec9SKyle Evans 294*f0865ec9SKyle Evans /* 295*f0865ec9SKyle Evans * NOT constant time with regard to the bitlength of exp. 296*f0865ec9SKyle Evans * Aliasing is supported. 297*f0865ec9SKyle Evans * 298*f0865ec9SKyle Evans * Compute (base ** exp) mod (mod) using a Montgomery Ladder algorithm. 299*f0865ec9SKyle Evans * Internally, this computes Montgomery coefficients and uses the redc 300*f0865ec9SKyle Evans * function. 301*f0865ec9SKyle Evans * 302*f0865ec9SKyle Evans * Returns 0 on success, -1 on error. 303*f0865ec9SKyle Evans */ 304*f0865ec9SKyle Evans int nn_mod_pow(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod) 305*f0865ec9SKyle Evans { 306*f0865ec9SKyle Evans nn r, r_square; 307*f0865ec9SKyle Evans word_t mpinv; 308*f0865ec9SKyle Evans int ret, isodd; 309*f0865ec9SKyle Evans r.magic = r_square.magic = WORD(0); 310*f0865ec9SKyle Evans 311*f0865ec9SKyle Evans /* Handle the case where our modulo is even: in this case, we cannot 312*f0865ec9SKyle Evans * use our Montgomery multiplication primitives as they are only suitable 313*f0865ec9SKyle Evans * for odd numbers. We fallback to less efficient regular modular exponentiation. 314*f0865ec9SKyle Evans */ 315*f0865ec9SKyle Evans ret = nn_isodd(mod, &isodd); EG(ret, err); 316*f0865ec9SKyle Evans if(!isodd){ 317*f0865ec9SKyle Evans /* mod is even: use the regular unoptimized modular exponentiation */ 318*f0865ec9SKyle Evans ret = _nn_mod_pow(out, base, exp, mod); 319*f0865ec9SKyle Evans } 320*f0865ec9SKyle Evans else{ 321*f0865ec9SKyle Evans /* mod is odd */ 322*f0865ec9SKyle Evans /* Compute the Montgomery coefficients */ 323*f0865ec9SKyle Evans ret = nn_compute_redc1_coefs(&r, &r_square, mod, &mpinv); EG(ret, err); 324*f0865ec9SKyle Evans 325*f0865ec9SKyle Evans /* Now use the redc version */ 326*f0865ec9SKyle Evans ret = nn_mod_pow_redc(out, base, exp, mod, &r, &r_square, mpinv); 327*f0865ec9SKyle Evans } 328*f0865ec9SKyle Evans 329*f0865ec9SKyle Evans err: 330*f0865ec9SKyle Evans nn_uninit(&r); 331*f0865ec9SKyle Evans nn_uninit(&r_square); 332*f0865ec9SKyle Evans 333*f0865ec9SKyle Evans return ret; 334*f0865ec9SKyle Evans } 335