bn_word.c: fix overflow bug in BN_add_word.
[openssl.git] / crypto / bn / bn_div.c
index ea099e0c2bb823d107f268621df1b62168391177..034a80a4a63f5f363b142e0cd53925971e8322fa 100644 (file)
@@ -56,6 +56,8 @@
  * [including the GNU Public Licence.]
  */
 
+#define OPENSSL_FIPSAPI
+
 #include <stdio.h>
 #include <openssl/bn.h>
 #include "cryptlib.h"
@@ -102,7 +104,7 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d,
        /* The next 2 are needed so we can do a dv->d[0]|=1 later
         * since BN_lshift1 will only work once there is a value :-) */
        BN_zero(dv);
-       bn_wexpand(dv,1);
+       if(bn_wexpand(dv,1) == NULL) goto end;
        dv->top=1;
 
        if (!BN_lshift(D,D,nm-nd)) goto end;
@@ -141,6 +143,7 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d,
     *
     *                                  <appro@fy.chalmers.se>
     */
+#undef bn_div_words
 #  define bn_div_words(n0,n1,d0)               \
        ({  asm volatile (                      \
                "divl   %4"                     \
@@ -155,6 +158,7 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d,
     * Same story here, but it's 128-bit by 64-bit division. Wow!
     *                                  <appro@fy.chalmers.se>
     */
+#  undef bn_div_words
 #  define bn_div_words(n0,n1,d0)               \
        ({  asm volatile (                      \
                "divq   %4"                     \
@@ -169,8 +173,8 @@ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d,
 #endif /* OPENSSL_NO_ASM */
 
 
-/* BN_div computes  dv := num / divisor,  rounding towards zero, and sets up
- * rm  such that  dv*divisor + rm = num  holds.
+/* BN_div computes  dv := num / divisor,  rounding towards
+ * zero, and sets up rm  such that  dv*divisor + rm = num  holds.
  * Thus:
  *     dv->neg == num->neg ^ divisor->neg  (unless the result is zero)
  *     rm->neg == num->neg                 (unless the remainder is zero)
@@ -179,13 +183,32 @@ 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;
+       int no_branch=0;
+
+       /* Invalid zero-padding would have particularly bad consequences
+        * in the case of 'num', so don't just rely on bn_check_top() for this one
+        * (bn_check_top() works only for BN_DEBUG builds) */
+       if (num->top > 0 && num->d[num->top - 1] == 0)
+               {
+               BNerr(BN_F_BN_DIV,BN_R_NOT_INITIALIZED);
+               return 0;
+               }
 
        bn_check_top(num);
+
+       if ((BN_get_flags(num, BN_FLG_CONSTTIME) != 0) || (BN_get_flags(divisor, BN_FLG_CONSTTIME) != 0))
+               {
+               no_branch=1;
+               }
+
+       bn_check_top(dv);
+       bn_check_top(rm);
+       /* bn_check_top(num); */ /* 'num' has been checked already */
        bn_check_top(divisor);
 
        if (BN_is_zero(divisor))
@@ -194,7 +217,7 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
                return(0);
                }
 
-       if (BN_ucmp(num,divisor) < 0)
+       if (!no_branch && BN_ucmp(num,divisor) < 0)
                {
                if (rm != NULL)
                        { if (BN_copy(rm,num) == NULL) return(0); }
@@ -209,8 +232,8 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
        if (dv == NULL)
                res=BN_CTX_get(ctx);
        else    res=dv;
-       if (sdiv == NULL || res == NULL) goto err;
-       tmp->neg=0;
+       if (sdiv == NULL || res == NULL || tmp == NULL || snum == NULL)
+               goto err;
 
        /* First we normalise the numbers */
        norm_shift=BN_BITS2-((BN_num_bits(divisor))%BN_BITS2);
@@ -219,20 +242,41 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
        norm_shift+=BN_BITS2;
        if (!(BN_lshift(snum,num,norm_shift))) goto err;
        snum->neg=0;
+
+       if (no_branch)
+               {
+               /* Since we don't know whether snum is larger than sdiv,
+                * we pad snum with enough zeroes without changing its
+                * value. 
+                */
+               if (snum->top <= sdiv->top+1) 
+                       {
+                       if (bn_wexpand(snum, sdiv->top + 2) == NULL) goto err;
+                       for (i = snum->top; i < sdiv->top + 2; i++) snum->d[i] = 0;
+                       snum->top = sdiv->top + 2;
+                       }
+               else
+                       {
+                       if (bn_wexpand(snum, snum->top + 1) == NULL) goto err;
+                       snum->d[snum->top] = 0;
+                       snum->top ++;
+                       }
+               }
+
        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];
 
@@ -242,27 +286,40 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
        /* Setup to 'res' */
        res->neg= (num->neg^divisor->neg);
        if (!bn_wexpand(res,(loop+1))) goto err;
-       res->top=loop;
+       res->top=loop-no_branch;
        resp= &(res->d[loop-1]);
 
        /* space for temp */
        if (!bn_wexpand(tmp,(div_n+1))) goto err;
 
-       if (BN_ucmp(&wnum,sdiv) >= 0)
+       if (!no_branch)
                {
-               if (!BN_usub(&wnum,&wnum,sdiv)) goto err;
-               *resp=1;
-               res->d[res->top-1]=1;
+               if (BN_ucmp(&wnum,sdiv) >= 0)
+                       {
+                       /* 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;
+                       }
+               else
+                       res->top--;
                }
-       else
-               res->top--;
+
+       /* if res->top == 0 then clear the neg value otherwise decrease
+        * the resp pointer */
        if (res->top == 0)
                res->neg = 0;
-       resp--;
+       else
+               resp--;
 
-       for (i=0; i<loop-1; i++)
+       for (i=0; i<loop-1; i++, wnump--, resp--)
                {
                BN_ULONG q,l0;
+               /* the first part of the loop uses the top two words of
+                * snum and sdiv to calculate a BN_ULONG q such that
+                * | wnum - sdiv * q | < sdiv */
 #if defined(BN_DIV3W) && !defined(OPENSSL_NO_ASM)
                BN_ULONG bn_div_3_words(BN_ULONG*,BN_ULONG,BN_ULONG);
                q=bn_div_3_words(wnump,d1,d0);
@@ -282,6 +339,11 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
                        q=(BN_ULONG)(((((BN_ULLONG)n0)<<BN_BITS2)|n1)/d0);
 #else
                        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
 #endif
 
 #ifndef REMAINDER_IS_ALREADY_CALCULATED
@@ -303,9 +365,14 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
                                t2 -= d1;
                                }
 #else /* !BN_LLONG */
-                       BN_ULONG t2l,t2h,ql,qh;
+                       BN_ULONG t2l,t2h;
 
                        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
@@ -316,9 +383,12 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
                        t2l = d1 * q;
                        t2h = BN_UMULT_HIGH(d1,q);
 #else
+                       {
+                       BN_ULONG ql, qh;
                        t2l=LBITS(d1); t2h=HBITS(d1);
                        ql =LBITS(q);  qh =HBITS(q);
                        mul64(t2l,t2h,ql,qh); /* t2=(BN_ULLONG)d1*q; */
+                       }
 #endif
 
                        for (;;)
@@ -336,27 +406,28 @@ 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;
-               if (!BN_sub(&wnum,&wnum,tmp)) goto err;
-
-               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;
-                       if (!BN_add(&wnum,&wnum,sdiv)) goto err;
-                       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
@@ -366,12 +437,14 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
                BN_rshift(rm,snum,norm_shift);
                if (!BN_is_zero(rm))
                        rm->neg = neg;
+               bn_check_top(rm);
                }
+       if (no_branch)  bn_correct_top(res);
        BN_CTX_end(ctx);
        return(1);
 err:
+       bn_check_top(rm);
        BN_CTX_end(ctx);
        return(0);
        }
-
 #endif