Our current implementations of BN_{,u}{add,sub}() are plain disgusting.
OpenSSL cleaned this up quite a bit recently -- still under
'(the "License")' -- and now you can actually follow what is going on.
There should be no performance impact from this change, the code is
doing essentially the same thing, just in a more orderly way.

For review, note that are two pairs of functions, BN_{add,sub}() and
BN_{uadd,usub}() which have similar changes. I recommend just applying
the diff and looking at the resulting file, the new functions are
pretty obvious.

Index: bn/bn_add.c
===================================================================
RCS file: /var/cvs/src/lib/libcrypto/bn/bn_add.c,v
retrieving revision 1.12
diff -u -p -r1.12 bn_add.c
--- bn/bn_add.c 10 Jun 2018 19:38:19 -0000      1.12
+++ bn/bn_add.c 11 Jul 2018 04:45:16 -0000
@@ -62,61 +62,51 @@
 
 #include "bn_lcl.h"
 
-/* r can == a or b */
 int
 BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
 {
-       const BIGNUM *tmp;
-       int a_neg = a->neg, ret;
+       int ret, r_neg;
 
        bn_check_top(a);
        bn_check_top(b);
 
-       /*  a +  b      a+b
-        *  a + -b      a-b
-        * -a +  b      b-a
-        * -a + -b      -(a+b)
-        */
-       if (a_neg ^ b->neg) {
-               /* only one is negative */
-               if (a_neg) {
-                       tmp = a;
-                       a = b;
-                       b = tmp;
-               }
-
-               /* we are now a - b */
+       if (a->neg == b->neg) {
+               r_neg = a->neg;
+               ret = BN_uadd(r, a, b);
+       } else {
+               int cmp = BN_ucmp(a, b);
 
-               if (BN_ucmp(a, b) < 0) {
-                       if (!BN_usub(r, b, a))
-                               return (0);
-                       r->neg = 1;
+               if (cmp > 0) {
+                       r_neg = a->neg;
+                       ret = BN_usub(r, a, b);
+               } else if (cmp < 0) {
+                       r_neg = b->neg;
+                       ret = BN_usub(r, b, a);
                } else {
-                       if (!BN_usub(r, a, b))
-                               return (0);
-                       r->neg = 0;
+                       r_neg = 0;
+                       BN_zero(r);
+                       ret = 1;
                }
-               return (1);
        }
 
-       ret = BN_uadd(r, a, b);
-       r->neg = a_neg;
+       r->neg = r_neg;
        bn_check_top(r);
        return ret;
 }
 
-/* unsigned add of b to a */
 int
 BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
 {
        int max, min, dif;
-       BN_ULONG *ap, *bp, *rp, carry, t1, t2;
-       const BIGNUM *tmp;
+       const BN_ULONG *ap, *bp;
+       BN_ULONG *rp, carry, t1, t2;
 
        bn_check_top(a);
        bn_check_top(b);
 
        if (a->top < b->top) {
+               const BIGNUM *tmp;
+
                tmp = a;
                a = b;
                b = tmp;
@@ -137,41 +127,28 @@ BN_uadd(BIGNUM *r, const BIGNUM *a, cons
        carry = bn_add_words(rp, ap, bp, min);
        rp += min;
        ap += min;
-       bp += min;
 
-       if (carry) {
-               while (dif) {
-                       dif--;
-                       t1 = *(ap++);
-                       t2 = (t1 + 1) & BN_MASK2;
-                       *(rp++) = t2;
-                       if (t2) {
-                               carry = 0;
-                               break;
-                       }
-               }
-               if (carry) {
-                       /* carry != 0 => dif == 0 */
-                       *rp = 1;
-                       r->top++;
-               }
+       while (dif) {
+               dif--;
+               t1 = *(ap++);
+               t2 = (t1 + carry) & BN_MASK2;
+               *(rp++) = t2;
+               carry &= (t2 == 0);
        }
-       if (dif && rp != ap)
-               while (dif--)
-                       /* copy remaining words if ap != rp */
-                       *(rp++) = *(ap++);
+       *rp = carry;
+       r->top += carry;
+
        r->neg = 0;
        bn_check_top(r);
        return 1;
 }
 
-/* unsigned subtraction of b from a, a must be larger than b. */
 int
 BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
 {
        int max, min, dif;
-       BN_ULONG t1, t2, *ap, *bp, *rp;
-       int i, carry;
+       const BN_ULONG *ap, *bp;
+       BN_ULONG t1, t2, borrow, *rp;
 
        bn_check_top(a);
        bn_check_top(b);
@@ -180,134 +157,67 @@ BN_usub(BIGNUM *r, const BIGNUM *a, cons
        min = b->top;
        dif = max - min;
 
-       if (dif < 0)    /* hmm... should not be happening */
-       {
+       if (dif < 0) {
                BNerror(BN_R_ARG2_LT_ARG3);
-               return (0);
+               return 0;
        }
 
        if (bn_wexpand(r, max) == NULL)
-               return (0);
+               return 0;
 
        ap = a->d;
        bp = b->d;
        rp = r->d;
 
-#if 1
-       carry = 0;
-       for (i = min; i != 0; i--) {
-               t1= *(ap++);
-               t2= *(bp++);
-               if (carry) {
-                       carry = (t1 <= t2);
-                       t1 = (t1 - t2 - 1)&BN_MASK2;
-               } else {
-                       carry = (t1 < t2);
-                       t1 = (t1 - t2)&BN_MASK2;
-               }
-               *(rp++) = t1&BN_MASK2;
-       }
-#else
-       carry = bn_sub_words(rp, ap, bp, min);
+       borrow = bn_sub_words(rp, ap, bp, min);
        ap += min;
-       bp += min;
        rp += min;
-#endif
-       if (carry) /* subtracted */
-       {
-               if (!dif)
-                       /* error: a < b */
-                       return 0;
-               while (dif) {
-                       dif--;
-                       t1 = *(ap++);
-                       t2 = (t1 - 1)&BN_MASK2;
-                       *(rp++) = t2;
-                       if (t1)
-                               break;
-               }
-       }
-#if 0
-       memcpy(rp, ap, sizeof(*rp)*(max - i));
-#else
-       if (rp != ap) {
-               for (;;) {
-                       if (!dif--)
-                               break;
-                       rp[0] = ap[0];
-                       if (!dif--)
-                               break;
-                       rp[1] = ap[1];
-                       if (!dif--)
-                               break;
-                       rp[2] = ap[2];
-                       if (!dif--)
-                               break;
-                       rp[3] = ap[3];
-                       rp += 4;
-                       ap += 4;
-               }
+
+       while (dif) {
+               dif--;
+               t1 = *(ap++);
+               t2 = (t1 - borrow) & BN_MASK2;
+               *(rp++) = t2;
+               borrow &= (t1 == 0);
        }
-#endif
+
+       while (max > 0 && *--rp == 0)
+               max--;
 
        r->top = max;
        r->neg = 0;
        bn_correct_top(r);
-       return (1);
+       return 1;
 }
 
 int
 BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
 {
-       int max;
-       int add = 0, neg = 0;
-       const BIGNUM *tmp;
+       int ret, r_neg;
 
        bn_check_top(a);
        bn_check_top(b);
 
-       /*  a -  b      a-b
-        *  a - -b      a+b
-        * -a -  b      -(a+b)
-        * -a - -b      b-a
-        */
-       if (a->neg) {
-               if (b->neg) {
-                       tmp = a;
-                       a = b;
-                       b = tmp;
-               } else {
-                       add = 1;
-                       neg = 1;
-               }
+       if (a->neg != b->neg) {
+               r_neg = a->neg;
+               ret = BN_uadd(r, a, b);
        } else {
-               if (b->neg) {
-                       add = 1;
-                       neg = 0;
-               }
-       }
+               int cmp = BN_ucmp(a, b);
 
-       if (add) {
-               if (!BN_uadd(r, a, b))
-                       return (0);
-               r->neg = neg;
-               return (1);
+               if (cmp > 0) {
+                       r_neg = a->neg;
+                       ret = BN_usub(r, a, b);
+               } else if (cmp < 0) {
+                       r_neg = !b->neg;
+                       ret = BN_usub(r, b, a);
+               } else {
+                       r_neg = 0;
+                       BN_zero(r);
+                       ret = 1;
+               }
        }
 
-       /* We are actually doing a - b :-) */
-
-       max = (a->top > b->top) ? a->top : b->top;
-       if (bn_wexpand(r, max) == NULL)
-               return (0);
-       if (BN_ucmp(a, b) < 0) {
-               if (!BN_usub(r, b, a))
-                       return (0);
-               r->neg = 1;
-       } else {
-               if (!BN_usub(r, a, b))
-                       return (0);
-               r->neg = 0;
-       }
+       r->neg = r_neg;
        bn_check_top(r);
-       return (1);
+       return ret;
 }

Reply via email to