X-Git-Url: https://git.openssl.org/gitweb/?a=blobdiff_plain;f=crypto%2Fbn%2Fbn_div.c;h=3b4392955e070e9d97e48b581d8079c34cfc9439;hb=9e051bac139ce07f846d88b90f3ec514c39ea416;hp=2e600c7c5426ef3a00566ca5ab89c35454bf9491;hpb=78a0c1f18d5a1f0e51b7467ef7b153b8c29fbb03;p=openssl.git diff --git a/crypto/bn/bn_div.c b/crypto/bn/bn_div.c index 2e600c7c54..3b4392955e 100644 --- a/crypto/bn/bn_div.c +++ b/crypto/bn/bn_div.c @@ -127,9 +127,10 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, #else -#if !defined(NO_ASM) && !defined(NO_INLINE_ASM) && !defined(PEDANTIC) && !defined(BN_DIV3W) +#if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) \ + && !defined(PEDANTIC) && !defined(BN_DIV3W) # if defined(__GNUC__) && __GNUC__>=2 -# if defined(__i386) +# if defined(__i386) || defined (__i386__) /* * There were two reasons for implementing this template: * - GNU C generates a call to a function (__udivdi3 to be exact) @@ -149,9 +150,23 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, q; \ }) # define REMAINDER_IS_ALREADY_CALCULATED +# elif defined(__x86_64) && defined(SIXTY_FOUR_BIT_LONG) + /* + * Same story here, but it's 128-bit by 64-bit division. Wow! + * + */ +# define bn_div_words(n0,n1,d0) \ + ({ asm volatile ( \ + "divq %4" \ + : "=a"(q), "=d"(rem) \ + : "a"(n1), "d"(n0), "g"(d0) \ + : "cc"); \ + q; \ + }) +# define REMAINDER_IS_ALREADY_CALCULATED # endif /* __ */ # endif /* __GNUC__ */ -#endif /* NO_ASM */ +#endif /* OPENSSL_NO_ASM */ /* BN_div computes dv := num / divisor, rounding towards zero, and sets up @@ -164,12 +179,16 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, BN_CTX *ctx) { - int norm_shift,i,j,loop; + int norm_shift,i,loop; BIGNUM *tmp,wnum,*snum,*sdiv,*res; BN_ULONG *resp,*wnump; BN_ULONG d0,d1; int num_n,div_n; + if (dv) + bn_check_top(dv); + if (rm) + bn_check_top(rm); bn_check_top(num); bn_check_top(divisor); @@ -195,29 +214,28 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, res=BN_CTX_get(ctx); else res=dv; if (sdiv == NULL || res == NULL) goto err; - tmp->neg=0; /* First we normalise the numbers */ norm_shift=BN_BITS2-((BN_num_bits(divisor))%BN_BITS2); - BN_lshift(sdiv,divisor,norm_shift); + if (!(BN_lshift(sdiv,divisor,norm_shift))) goto err; sdiv->neg=0; norm_shift+=BN_BITS2; - BN_lshift(snum,num,norm_shift); + if (!(BN_lshift(snum,num,norm_shift))) goto err; snum->neg=0; div_n=sdiv->top; num_n=snum->top; loop=num_n-div_n; - /* Lets setup a 'window' into snum * This is the part that corresponds to the current * 'area' being divided */ - BN_init(&wnum); - wnum.d= &(snum->d[loop]); - wnum.top= div_n; - wnum.dmax= snum->dmax+1; /* a bit of a lie */ + wnum.neg = 0; + wnum.d = &(snum->d[loop]); + wnum.top = div_n; + /* only needed when BN_ucmp messes up the values between top and max */ + wnum.dmax = snum->dmax - loop; /* so we don't step out of bounds */ /* Get the top 2 words of sdiv */ - /* i=sdiv->top; */ + /* div_n=sdiv->top; */ d0=sdiv->d[div_n-1]; d1=(div_n == 1)?0:sdiv->d[div_n-2]; @@ -235,18 +253,30 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, if (BN_ucmp(&wnum,sdiv) >= 0) { - if (!BN_usub(&wnum,&wnum,sdiv)) goto err; + /* If BN_DEBUG_RAND is defined BN_ucmp changes (via + * bn_pollute) the const bignum arguments => + * clean the values between top and max again */ + bn_clear_top2max(&wnum); + bn_sub_words(wnum.d, wnum.d, sdiv->d, div_n); *resp=1; - res->d[res->top-1]=1; } else res->top--; - resp--; + /* if res->top == 0 then clear the neg value otherwise decrease + * the resp pointer */ + if (res->top == 0) + res->neg = 0; + else + resp--; - for (i=0; i 0x%08X\n", + n0, n1, d0, q); +#endif #endif #ifndef REMAINDER_IS_ALREADY_CALCULATED @@ -288,11 +323,18 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, BN_ULONG t2l,t2h,ql,qh; q=bn_div_words(n0,n1,d0); +#ifdef BN_DEBUG_LEVITTE + fprintf(stderr,"DEBUG: bn_div_words(0x%08X,0x%08X,0x%08\ +X) -> 0x%08X\n", + n0, n1, d0, q); +#endif #ifndef REMAINDER_IS_ALREADY_CALCULATED rem=(n1-q*d0)&BN_MASK2; #endif -#ifdef BN_UMULT_HIGH +#if defined(BN_UMULT_LOHI) + BN_UMULT_LOHI(t2l,t2h,d1,q); +#elif defined(BN_UMULT_HIGH) t2l = d1 * q; t2h = BN_UMULT_HIGH(d1,q); #else @@ -316,36 +358,44 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, #endif /* !BN_DIV3W */ l0=bn_mul_words(tmp->d,sdiv->d,div_n,q); - wnum.d--; wnum.top++; tmp->d[div_n]=l0; - for (j=div_n+1; j>0; j--) - if (tmp->d[j-1]) break; - tmp->top=j; - - j=wnum.top; - BN_sub(&wnum,&wnum,tmp); - - snum->top=snum->top+wnum.top-j; - - if (wnum.neg) + wnum.d--; + /* ingore top values of the bignums just sub the two + * BN_ULONG arrays with bn_sub_words */ + if (bn_sub_words(wnum.d, wnum.d, tmp->d, div_n+1)) { + /* Note: As we have considered only the leading + * two BN_ULONGs in the calculation of q, sdiv * q + * might be greater than wnum (but then (q-1) * sdiv + * is less or equal than wnum) + */ q--; - j=wnum.top; - BN_add(&wnum,&wnum,sdiv); - snum->top+=wnum.top-j; + if (bn_add_words(wnum.d, wnum.d, sdiv->d, div_n)) + /* we can't have an overflow here (assuming + * that q != 0, but if q == 0 then tmp is + * zero anyway) */ + (*wnump)++; } - *(resp--)=q; - wnump--; + /* store part of the result */ + *resp = q; } + bn_correct_top(snum); if (rm != NULL) { + /* Keep a copy of the neg flag in num because if rm==num + * BN_rshift() will overwrite it. + */ + int neg = num->neg; BN_rshift(rm,snum,norm_shift); if (!BN_is_zero(rm)) - rm->neg = num->neg; + rm->neg = neg; + bn_check_top(rm); } BN_CTX_end(ctx); return(1); err: + if (rm) + bn_check_top(rm); BN_CTX_end(ctx); return(0); }