Commit 7d0d0996 authored by Bodo Möller's avatar Bodo Möller
Browse files

binary algorithm for modular inversion

parent 9cddbf14
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -4,6 +4,13 @@

 Changes between 0.9.6 and 0.9.7  [xx XXX 2000]

  *) Implement binary inversion algorithm for BN_mod_inverse in addition
     to the algorithm using long divison.  The binary algorithm can be
     used only if the modulus is odd.  It is faster only for relatively
     small moduli (roughly 20% for 128-bit moduli, roughly 5% for 256-bit
     moduli), so we use it only for moduli up to 400 bits.
     [Bodo Moeller]

  *) Change bctest again: '-x' expressions are not available in all
     versions of 'test'.
     [Bodo Moeller]
+181 −96
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@
 * [including the GNU Public Licence.]
 */
/* ====================================================================
 * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
 * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
@@ -240,18 +240,102 @@ BIGNUM *BN_mod_inverse(BIGNUM *in,
	/* From  B = a mod |n|,  A = |n|  it follows that
	 *
	 *      0 <= B < A,
	 *      sign*X*a  ==  B   (mod |n|),
	 *     -sign*Y*a  ==  A   (mod |n|).
	 *     -sign*X*a  ==  B   (mod |n|),
	 *      sign*Y*a  ==  A   (mod |n|).
	 */

	if (BN_is_odd(n) && (BN_num_bits(n) <= 400))
		{
		/* Binary inversion algorithm; requires odd modulus.
		 * This is faster than the general algorithm if the modulus
		 * is sufficiently small. */
		int shift;
		
		while (!BN_is_zero(B))
			{
			/*
			 *      0 < B < A <= |n|,
			 * (1) -sign*X*a  ==  B   (mod |n|),
			 * (2)  sign*Y*a  ==  A   (mod |n|)
			 */

			/* Now divide  B  by the maximum possible power of two in the integers,
			 * and divide  X  by the same value mod |n|.
			 * When we're done, (1) still holds. */
			shift = 0;
			while (!BN_is_bit_set(B, shift)) /* note that 0 < B */
				{
				shift++;
				
				if (BN_is_odd(X))
					{
					if (!BN_uadd(X, X, n)) goto err;
					}
				/* now X is even, so we can easily divide it by two */
				if (!BN_rshift1(X, X)) goto err;
				}
			if (shift > 0)
				{
				if (!BN_rshift(B, B, shift)) goto err;
				}


			/* Same for  A  and  Y.  Afterwards, (2) still holds. */
			shift = 0;
			while (!BN_is_bit_set(A, shift)) /* note that 0 < A */
				{
				shift++;
				
				if (BN_is_odd(Y))
					{
					if (!BN_uadd(Y, Y, n)) goto err;
					}
				/* now Y is even */
				if (!BN_rshift1(Y, Y)) goto err;
				}
			if (shift > 0)
				{
				if (!BN_rshift(A, A, shift)) goto err;
				}

			
			/* We still have (1) and (2), but  A  may no longer be larger than  B.
			 * Both  A  and  B  are odd.
			 * The following computations ensure that
			 *
			 *      0 =< B < A = |n|,
			 * (1) -sign*X*a  ==  B   (mod |n|),
			 * (2)  sign*Y*a  ==  A   (mod |n|)
			 */
			if (BN_ucmp(B, A) >= 0)
				{
				/* -sign*(X + Y)*a == B - A  (mod |n|) */
				if (!BN_uadd(X, X, Y)) goto err;
				/* NB: we could use BN_mod_add_quick(X, X, Y, n), but that
				 * actually makes the algorithm slower */
				if (!BN_usub(B, B, A)) goto err;
				}
			else
				{
				/*  sign*(X + Y)*a == A - B  (mod |n|) */
				if (!BN_uadd(Y, Y, X)) goto err;
				/* as above, BN_mod_add_quick(Y, Y, X, n) would slow things down */
				if (!BN_usub(A, A, B)) goto err;
				}
			}
		}
	else
		{
		/* general inversion algorithm (less efficient than binary inversion) */

		while (!BN_is_zero(B))
			{
			BIGNUM *tmp;
			
			/*
			 *      0 < B < A,
		 * (*)  sign*X*a  ==  B   (mod |n|),
		 *     -sign*Y*a  ==  A   (mod |n|)
			 * (*) -sign*X*a  ==  B   (mod |n|),
			 *      sign*Y*a  ==  A   (mod |n|)
			 */
			
			/* (D, M) := (A/B, A%B) ... */
@@ -298,7 +382,7 @@ BIGNUM *BN_mod_inverse(BIGNUM *in,
			/* Now
			 *      A = D*B + M;
			 * thus we have
		 * (**) -sign*Y*a  ==  D*B + M   (mod |n|).
			 * (**)  sign*Y*a  ==  D*B + M   (mod |n|).
			 */
			
			tmp=A; /* keep the BIGNUM object, the value does not matter */
@@ -310,20 +394,20 @@ BIGNUM *BN_mod_inverse(BIGNUM *in,
			
			/* Since the former  M  is now  B  and the former  B  is now  A,
			 * (**) translates into
		 *      -sign*Y*a  ==  D*A + B    (mod |n|),
			 *       sign*Y*a  ==  D*A + B    (mod |n|),
			 * i.e.
		 *      -sign*Y*a - D*A  ==  B    (mod |n|).
			 *       sign*Y*a - D*A  ==  B    (mod |n|).
			 * Similarly, (*) translates into
		 *       sign*X*a  ==  A          (mod |n|).
			 *      -sign*X*a  ==  A          (mod |n|).
			 *
			 * Thus,
		 *  -sign*Y*a - D*sign*X*a  ==  B  (mod |n|),
			 *   sign*Y*a + D*sign*X*a  ==  B  (mod |n|),
			 * i.e.
		 *       -sign*(Y + D*X)*a  ==  B  (mod |n|).
			 *        sign*(Y + D*X)*a  ==  B  (mod |n|).
			 *
			 * So if we set  (X, Y, sign) := (Y + D*X, X, -sign),  we arrive back at
		 *       sign*X*a  ==  B   (mod |n|),
		 *      -sign*Y*a  ==  A   (mod |n|).
			 *      -sign*X*a  ==  B   (mod |n|),
			 *       sign*Y*a  ==  A   (mod |n|).
			 * Note that  X  and  Y  stay non-negative all the time.
			 */
			
@@ -359,12 +443,13 @@ BIGNUM *BN_mod_inverse(BIGNUM *in,
			X=tmp;
			sign = -sign;
			}
		}
		
	/*
	 * The while loop (Euclid's algorithm) ends when
	 *      A == gcd(a,n);
	 * we have
	 *      -sign*Y*a  ==  A  (mod |n|),
	 *       sign*Y*a  ==  A  (mod |n|),
	 * where  Y  is non-negative.
	 */

@@ -378,7 +463,7 @@ BIGNUM *BN_mod_inverse(BIGNUM *in,
	if (BN_is_one(A))
		{
		/* Y*a == 1  (mod |n|) */
		if (BN_ucmp(Y,n) < 0)
		if (!Y->neg && BN_ucmp(Y,n) < 0)
			{
			if (!BN_copy(R,Y)) goto err;
			}
+2 −2
Original line number Diff line number Diff line
@@ -150,8 +150,8 @@ int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_
int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m)
	{
	if (!BN_add(r, a, b)) return 0;
	if (BN_cmp(r, m) >= 0)
		return BN_sub(r, r, m);
	if (BN_ucmp(r, m) >= 0)
		return BN_usub(r, r, m);
	return 1;
	}