fix BN_mod_word and give a more reasonable return value if an error occurred
[openssl.git] / crypto / bn / bn_word.c
index 4b3d0f011d20e44fa7872421f6422369f1abece3..ee7b87c45ccd38839db6261ed81c2d87b9e881b5 100644 (file)
@@ -60,9 +60,7 @@
 #include "cryptlib.h"
 #include "bn_lcl.h"
 
 #include "cryptlib.h"
 #include "bn_lcl.h"
 
-BN_ULONG BN_mod_word(a, w)
-BIGNUM *a;
-unsigned long w;
+BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w)
        {
 #ifndef BN_LLONG
        BN_ULONG ret=0;
        {
 #ifndef BN_LLONG
        BN_ULONG ret=0;
@@ -71,12 +69,16 @@ unsigned long w;
 #endif
        int i;
 
 #endif
        int i;
 
+       if (w == 0)
+               return (BN_ULONG)-1;
+
+       bn_check_top(a);
        w&=BN_MASK2;
        for (i=a->top-1; i>=0; i--)
                {
 #ifndef BN_LLONG
        w&=BN_MASK2;
        for (i=a->top-1; i>=0; i--)
                {
 #ifndef BN_LLONG
-               ret=((ret<<BN_BITS4)|((a->d[i]>>BN_BITS4)&BN_MASK2l))%(unsigned long)w;
-               ret=((ret<<BN_BITS4)|(a->d[i]&BN_MASK2l))%(unsigned long)w;
+               ret=((ret<<BN_BITS4)|((a->d[i]>>BN_BITS4)&BN_MASK2l))%w;
+               ret=((ret<<BN_BITS4)|(a->d[i]&BN_MASK2l))%w;
 #else
                ret=(BN_ULLONG)(((ret<<(BN_ULLONG)BN_BITS2)|a->d[i])%
                        (BN_ULLONG)w);
 #else
                ret=(BN_ULLONG)(((ret<<(BN_ULLONG)BN_BITS2)|a->d[i])%
                        (BN_ULLONG)w);
@@ -85,51 +87,74 @@ unsigned long w;
        return((BN_ULONG)ret);
        }
 
        return((BN_ULONG)ret);
        }
 
-BN_ULONG BN_div_word(a, w)
-BIGNUM *a;
-unsigned long w;
+BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w)
        {
        {
-       BN_ULONG ret;
-       int i;
+       BN_ULONG ret = 0;
+       int i, j;
+
+       bn_check_top(a);
+       w &= BN_MASK2;
+
+       if (!w)
+               /* actually this an error (division by zero) */
+               return (BN_ULONG)-1;
+       if (a->top == 0)
+               return 0;
+
+       /* normalize input (so bn_div_words doesn't complain) */
+       j = BN_BITS2 - BN_num_bits_word(w);
+       w <<= j;
+       if (!BN_lshift(a, a, j))
+               return (BN_ULONG)-1;
 
 
-       if (a->top == 0) return(0);
-       ret=0;
-       w&=BN_MASK2;
        for (i=a->top-1; i>=0; i--)
                {
                BN_ULONG l,d;
                
                l=a->d[i];
        for (i=a->top-1; i>=0; i--)
                {
                BN_ULONG l,d;
                
                l=a->d[i];
-               d=bn_div64(ret,l,w);
+               d=bn_div_words(ret,l,w);
                ret=(l-((d*w)&BN_MASK2))&BN_MASK2;
                a->d[i]=d;
                }
                ret=(l-((d*w)&BN_MASK2))&BN_MASK2;
                a->d[i]=d;
                }
-       if (a->d[a->top-1] == 0)
+       if ((a->top > 0) && (a->d[a->top-1] == 0))
                a->top--;
                a->top--;
+       ret >>= j;
+       bn_check_top(a);
        return(ret);
        }
 
        return(ret);
        }
 
-int BN_add_word(a, w)
-BIGNUM *a;
-unsigned long w;
+int BN_add_word(BIGNUM *a, BN_ULONG w)
        {
        BN_ULONG l;
        int i;
 
        {
        BN_ULONG l;
        int i;
 
+       bn_check_top(a);
+       w &= BN_MASK2;
+
+       /* degenerate case: w is zero */
+       if (!w) return 1;
+       /* degenerate case: a is zero */
+       if(BN_is_zero(a)) return BN_set_word(a, w);
+       /* handle 'a' when negative */
        if (a->neg)
                {
                a->neg=0;
                i=BN_sub_word(a,w);
                if (!BN_is_zero(a))
        if (a->neg)
                {
                a->neg=0;
                i=BN_sub_word(a,w);
                if (!BN_is_zero(a))
-                       a->neg=1;
+                       a->neg=!(a->neg);
                return(i);
                }
                return(i);
                }
-       w&=BN_MASK2;
-       if (bn_wexpand(a,a->top+1) == NULL) return(0);
+       /* Only expand (and risk failing) if it's possibly necessary */
+       if (((BN_ULONG)(a->d[a->top - 1] + 1) == 0) &&
+                       (bn_wexpand(a,a->top+1) == NULL))
+               return(0);
        i=0;
        for (;;)
                {
        i=0;
        for (;;)
                {
-               l=(a->d[i]+(BN_ULONG)w)&BN_MASK2;
+               if (i >= a->top)
+                       l=w;
+               else
+                       l=(a->d[i]+w)&BN_MASK2;
                a->d[i]=l;
                if (w > l)
                        w=1;
                a->d[i]=l;
                if (w > l)
                        w=1;
@@ -139,15 +164,28 @@ unsigned long w;
                }
        if (i >= a->top)
                a->top++;
                }
        if (i >= a->top)
                a->top++;
+       bn_check_top(a);
        return(1);
        }
 
        return(1);
        }
 
-int BN_sub_word(a, w)
-BIGNUM *a;
-unsigned long w;
+int BN_sub_word(BIGNUM *a, BN_ULONG w)
        {
        int i;
 
        {
        int i;
 
+       bn_check_top(a);
+       w &= BN_MASK2;
+
+       /* degenerate case: w is zero */
+       if (!w) return 1;
+       /* degenerate case: a is zero */
+       if(BN_is_zero(a))
+               {
+               i = BN_set_word(a,w);
+               if (i != 0)
+                       BN_set_negative(a, 1);
+               return i;
+               }
+       /* handle 'a' when negative */
        if (a->neg)
                {
                a->neg=0;
        if (a->neg)
                {
                a->neg=0;
@@ -156,7 +194,6 @@ unsigned long w;
                return(i);
                }
 
                return(i);
                }
 
-       w&=BN_MASK2;
        if ((a->top == 1) && (a->d[0] < w))
                {
                a->d[0]=w-a->d[0];
        if ((a->top == 1) && (a->d[0] < w))
                {
                a->d[0]=w-a->d[0];
@@ -180,25 +217,31 @@ unsigned long w;
                }
        if ((a->d[i] == 0) && (i == (a->top-1)))
                a->top--;
                }
        if ((a->d[i] == 0) && (i == (a->top-1)))
                a->top--;
+       bn_check_top(a);
        return(1);
        }
 
        return(1);
        }
 
-int BN_mul_word(a,w)
-BIGNUM *a;
-unsigned long w;
+int BN_mul_word(BIGNUM *a, BN_ULONG w)
        {
        BN_ULONG ll;
 
        {
        BN_ULONG ll;
 
+       bn_check_top(a);
        w&=BN_MASK2;
        if (a->top)
                {
        w&=BN_MASK2;
        if (a->top)
                {
-               ll=bn_mul_words(a->d,a->d,a->top,w);
-               if (ll)
+               if (w == 0)
+                       BN_zero(a);
+               else
                        {
                        {
-                       if (bn_wexpand(a,a->top+1) == NULL) return(0);
-                       a->d[a->top++]=ll;
+                       ll=bn_mul_words(a->d,a->d,a->top,w);
+                       if (ll)
+                               {
+                               if (bn_wexpand(a,a->top+1) == NULL) return(0);
+                               a->d[a->top++]=ll;
+                               }
                        }
                }
                        }
                }
-       return(0);
+       bn_check_top(a);
+       return(1);
        }
 
        }