/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss or reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <nanu-nanu.h>

/****************************************************************************/
/*						Local prototypes									*/
/****************************************************************************/

static	int		NanuMexpNorm( BC_NANU nanu );
static	int		NanuMexpMont( BC_NANU nanu );
static unsigned	InvModInv( unsigned x );
static int		_bc_NanuMont(BC_NANU nanu, int aop, int bop, int mod, int inv);
static void		NanuMont( unsigned *res, unsigned *aop, unsigned *bop,
						  unsigned *mod, unsigned aln,  unsigned bln,
						  unsigned mln,  unsigned rem );

/****************************************************************************/
/*						Public API functions								*/
/****************************************************************************/

/** @addtogroup nanu */
/** @{ */

/**
*	Modular exponentiation.
*
*	Raises the third element on the stack to the power of the second
*	element of the stack and calculates the remainder of dividing the
*	result with the topmost element. All three elements are discarded
*	and the remainder is pushed onto the stack.
*
*	@param	nanu			The natural number machine state descriptor
*
*	@retval	BC_NANU_NANU	The operation succeeded
*	@retval	BC_NANU_SUND	The stack didn't have at least two items on it
*	@retval	BC_NANU_SOVR	Stack overflow occured during calculations
*	@retval	BC_NANU_OMEM	Out of memory pool space
*	@retval BC_NANU_NANN	The modulus was less than 2
*
*	@par Stack image
*		m e a ...  ==> a<sup>e</sup>\%m ...
*/

int		BC_NanuMexp( BC_NANU nanu )
{
unsigned 	t;
int			e;

	// Do a modular reduction, to guarantee that we never return
	// a number larger than the modulus. The stack now is m e a.

	if ( ( e = BC_NanuRoll( nanu, 2 ) ) ||				// a m e
		 ( e = BC_NanuOver( nanu ) ) ||					// m a m e
		 ( e = BC_NanuRmod( nanu ) ) ||					// a' m e
		 ( e = BC_NanuRoll( nanu, -2 ) ) )	{			// m e a'

		return( e );
	}

	// If a == 0 or a == 1 then the result is a.

	e = _bc_NanuStkSee( nanu, 2 );

	if ( _bc_NanuVarLen( nanu, e ) == 1 ) {

		if ( *_bc_NanuVarPtr( nanu, e ) < 2 ) {

			BC_NanuDrop( nanu );
			BC_NanuDrop( nanu );
			return( BC_NANU_NANU );
		}
	}

	// If e == 1, then the result is a.
	// If e == 0, then the result is 1.

	e = _bc_NanuStkSee( nanu, 1 );
	t = *_bc_NanuVarPtr( nanu, e );

	if ( _bc_NanuVarLen( nanu, e ) == 1 ) {

		if ( t == 1 ) {

			BC_NanuDrop( nanu );
			BC_NanuDrop( nanu );
			return( BC_NANU_NANU );
		}

		if ( t == 0 ) {

			*((char *) &t) = 1;
			BC_NanuDrop( nanu );
			BC_NanuDrop( nanu );
			BC_NanuDrop( nanu );
			BC_NanuPush( nanu, &t, 1 );
			return( BC_NANU_NANU );
		}
	}

	e = _bc_NanuStkSee( nanu, 0 );
	t = *_bc_NanuVarPtr( nanu, e );

	if ( t & 1 ) {

		// The modulus is odd , we use the Montgomery multiplication

		return( _bc_NanuError( nanu, NanuMexpMont( nanu ) ) );
	}
	else {

		// The exponent is even, we must use the common
		// or garden variety modular multiplication.

		return( _bc_NanuError( nanu, NanuMexpNorm( nanu ) ) );
	}
}

/** @} */

/****************************************************************************/
/*					Local functions											*/
/****************************************************************************/

/*
*	Modular exponentiation using regular modular multiplication
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		NanuMexpNorm( BC_NANU nanu )
{
int			mod, exp, pow, res, new;
unsigned	wrd, cnt, len, bit;

	// Pop the variables from the stack

	mod = _bc_NanuStkGet( nanu );
	exp = _bc_NanuStkGet( nanu );
	pow = _bc_NanuStkGet( nanu );

	// Get the length of the exponent and indicate
	// the we don't have a result yet.

	len = _bc_NanuVarLen( nanu, exp );
	res = -1;

	// Go through the words of the exponent

	for ( cnt = 0 ; cnt < len ; cnt++ ) {

		// Fetch the word of the exponent

		wrd = cnt[ _bc_NanuVarPtr( nanu, exp ) ];

		// Go through the bits of the current exponent word

		for ( bit = 33 ; --bit ; ) {

			// See if we have to multiply the result

			if ( wrd & 1 ) {

				// Yes, the result should be multiplied with the
				// current 2^n-th power of the number

				if ( res >= 0 ) {

					// We already have a result, do a multiply

					new = _bc_NanuMmul( nanu, pow, res, mod );
					if ( new < 0 ) return( new );
					_bc_NanuVarDec( nanu, res );
					res = new;
				}
				else {

					// We don't have the result yet, now we
					// create one by copying the power into it.

					res = _bc_NanuVarNew( nanu, _bc_NanuVarLen( nanu, pow ) );
					if ( res < 0 ) return( res );
					_bc_NanuMove( _bc_NanuVarPtr( nanu, res ),
								  _bc_NanuVarPtr( nanu, pow ),
								  _bc_NanuVarLen( nanu, pow ) );
				}
			}

			// Get rid of the current bit of the exponent

			wrd >>= 1;

			// If there are no more bits and this was the last word,
			// then we're done.

			if ( ! wrd && cnt == len - 1 ) break;

			// Otherwise, we have to square the power

			new = _bc_NanuMmul( nanu, pow, pow, mod );
			_bc_NanuVarDec( nanu, pow );
			pow = new;
		}
	}

	// Release the variables that we popped and push the result

	_bc_NanuVarDec( nanu, pow );
	_bc_NanuVarDec( nanu, mod );
	_bc_NanuVarDec( nanu, exp );
	_bc_NanuStkPut( nanu, res );
	return( BC_NANU_NANU );
}

/*
*	Modular exponentiation using Montgomery multiplication
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		NanuMexpMont( BC_NANU nanu )
{
int			mod, exp, pow, res, new;
unsigned	wrd, cnt, inv, len, bit;

	// Pop the variables from the stack

	mod = _bc_NanuStkGet( nanu );
	exp = _bc_NanuStkGet( nanu );
	pow = _bc_NanuStkGet( nanu );

	// Calculate the m' value for the multiplication

	inv = InvModInv( *_bc_NanuVarPtr( nanu, mod ) );

	// We need to transform the operand into Montgomery form.
	// To do that, we need a temporary that is one word longer
	// than the modulus (it will be R).

	len = _bc_NanuVarLen( nanu, mod );
	if ( ( new = _bc_NanuVarNew( nanu, len + 1 ) ) < 0 ) return( new );

	// Now create R

	_bc_NanuZero( _bc_NanuVarPtr( nanu, new ), len );
	_bc_NanuVarPtr( nanu, new )[ len ] = 1;

	// We now do a modular multiplication of ( a * R ) % m.
	// Since it is the B operand that can be of arbitrary length,
	// that will be R.

	if ( ( res = _bc_NanuMmul( nanu, pow, new, mod ) ) < 0 ) return( res );

	// We don't need R or a any more, we only use a'

	_bc_NanuVarDec( nanu, new );
	_bc_NanuVarDec( nanu, pow );
	pow = res;

	// We can start the exponentiation, pretty much the same thing as
	// with normal exponentiation, only we use the Montgomery multiply.
	// Get the length of the exponent and indicate the we don't have
	// a result yet.

	len = _bc_NanuVarLen( nanu, exp );
	res = -1;

	// Go through the words of the exponent

	for ( cnt = 0 ; cnt < len ; cnt++ ) {

		// Fetch the word of the exponent

		wrd = cnt[ _bc_NanuVarPtr( nanu, exp ) ];

		// Go through the bits of the current exponent word

		for ( bit = 33 ; --bit ; ) {

			// See if we have to multiply the result

			if ( wrd & 1 ) {

				// Yes, the result should be multiplied with the
				// current 2^n-th power of the number

				if ( res >= 0 ) {

					// We already have a result, do a multiply

					new = _bc_NanuMont( nanu, pow, res, mod, inv );
					if ( new < 0 ) return( new );
					_bc_NanuVarDec( nanu, res );
					res = new;
				}
				else {

					// We don't have the result yet, now we
					// create one by copying the power into it.

					res = _bc_NanuVarNew( nanu, _bc_NanuVarLen( nanu, pow ) );
					if ( res < 0 ) return( res );
					_bc_NanuMove( _bc_NanuVarPtr( nanu, res ),
								  _bc_NanuVarPtr( nanu, pow ),
								  _bc_NanuVarLen( nanu, pow ) );
				}
			}

			// Get rid of the current bit of the exponent

			wrd >>= 1;

			// If there are no more bits and this was the last word,
			// then we're done.

			if ( ! wrd && cnt == len - 1 ) break;

			// Otherwise, we have to square the power

			new = _bc_NanuMont( nanu, pow, pow, mod, inv );
			if ( new < 0 ) return( new );
			_bc_NanuVarDec( nanu, pow );
			pow = new;
		}
	}

	// The exponent and the power variables are not needed any more

	_bc_NanuVarDec( nanu, pow );
	_bc_NanuVarDec( nanu, exp );

	// We need one more Montgomery multiply to convert
	// the result back to the normal domain. Create a
	// new variable that will hold '1'.

	if ( ( pow = _bc_NanuVarNew( nanu, 1 ) ) < 0 ) return( pow );
	*_bc_NanuVarPtr( nanu, pow ) = 1;

	// Now do the Montgomery multiply

	new = _bc_NanuMont( nanu, res, pow, mod, inv );
	if ( new < 0 ) return( new );

	// Release all variables but the final result (which is in 'new'),
	// push the result onto the stack

	_bc_NanuVarDec( nanu, mod );
	_bc_NanuVarDec( nanu, res );
	_bc_NanuVarDec( nanu, pow );
	_bc_NanuStkPut( nanu, new );
	return( BC_NANU_NANU );
}

/*
*	Calculates the -m^-1 over 2^32 value
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	This value is used for the Montgomery multiplication.
*	It is actually of the arithmetic inverse of the multiplicative
*	inverse of a number with respect to the modulus of 2^32, that is,
*	if the modulus is m, then this function first calculates a number
*	z so that
*
*	( z * m ) % 2^32 == 1
*
*	and then it calculates the result r so that
*
*	( z + r ) % 2^32 == 0
*
*	As it turns out, you don't need m to calculate this, it is enough if
*	you know the lowest 32 bits of m, as the higher order bits will have
*	no effect. So the argument to this function is the lowest order word
*	of the modulus m.
*/

static unsigned	InvModInv( unsigned mod )
{
unsigned  res, tmp, bit, msk;

    tmp = mod;
    res = 1;
    bit = 1;
    msk = 1;

    while ( bit ) {

        mod <<= 1;
        bit <<= 1;
        msk  += msk + 1;

        if ( bit < ( tmp & msk ) ) {

            res |= bit;
            tmp += mod;
        }
    }

    return( -res );
}

/*
*	Montgomery multiplication
*	~~~~~~~~~~~~~~~~~~~~~~~~~
*	The Montgomery multiplication assumes that mod, the modulus is odd,
*	that aop, the first operand is smaller than the modulus. The second
*	operand, bop, can be of arbitrary length. Tha argument 'rem' is the
*	additive inverse of the multiplicative inverse of the modulus modulo
*	2^32.
*
*	The function calculates and returns, in res, the result of
*
*	res = ( aop * bop * R^-1 ) % mod
*
*	where R^-1 is the multiplicative inverse of the smallest power of 2^32
*	that is larger than the modulus, modulo the modulus. It does is faster
*	than a regular modular multiplication would calculate aop * bop % mod.
*	The following is a brief description of what it is actually good for.
*
*	The Montgomery m-residual form of a number x, by definition, is
*
*	r = ( x * R^-1 ) % m
*
*	where x and r are two numbers, R is the smallest power of some base
*	which is relative prime to the modulus m, that is, GCD( R, m ) == 1
*	(GCD() is the greatest common divisor) so that R > m. In our case
*	the base is 2^32 (i.e. a 32-bit word), thus if the modulus can be
*	stored in n 32-bit words, R is (2^32)^n. The GCD( R, m ) == 1 will
*	be true iff m is odd. R^-1 is the multiplicative inverse of R in m,
*	that is, R^-1 < m and ( R * R^-1 ) % m == 1.
*
*	Let's denote the Montgomery multiplication with the '@' symbol.
*	By definition, the Montgomery product of two numbers is the
*	Montgomery m-residual form of their ordinary product:
*
*	a @ b = ( ( a * b ) * R^-1 ) % m
*
*	Let us now introduce a transformation, denoted by the ' symbol:
*
*	x' = ( x * R ) % m
*
*	For such transformed numbers there are some important identities,
*	you can prove them by simply substituting the above definitions at
*	the relevant places. 1' means the multiplicative unity element of
*	the Montgomery domain.
*
*	1' = R % m
*	a' = ( a * 1' ) % m
*	a' = a' @ 1'
*	a  = a' @ 1
*
*	a' + b' = ( a + b )'
*	a' @ b' = ( a * b )'
*
*	So, if you have 2 numbers, a and b and you want to multiply them
*	using Montgomery multiplication, you have to:
*
*	1)	a' = ( a * R ) % m
*	2)	b' = ( b * R ) % m
*	3)	r' = a' @ b'
*	4)	r  = r' @ 1
*
*	Thus, we needed two regular modular multiplications and two Montgomery
*	ones to work out one regular modular multiply. Indeed, you would not
*	use the Montgomery multiply to actually multiply numbers. What you
*	use it for is exponentiation. Here's how you exponentiate:
*
*	x = a ^ e % m :
*
*	t = 1
*	for ( b = all bits of e LSB to MSB ) {
*		if ( b ) t = ( t * a ) % m;
*		a = ( a * a ) % m;
*	}
*	return( t );
*
*	That is, you execute one or two modular multiplications for each bit
*	of the exponent. If you want to use Montgomery, this how you do it:
*
*	t = R % m;
*	a = ( a * R ) % m;
*	for ( b = all bits of e LSB to MSB ) {
*		if ( b ) t = t @ a;
*		a = a @ a;
*	}
*	t = t @ 1;
*	return( t );
*
*	Thus, you need as many Montgomery multiplies as you needed regular
*	modular ones, plus a regular modular one plus an additional Montgomery.
*	The t = R % m; bit can be avoided the obvious way. Therefore, if the
*	exponent has many bits and the Montgomery multiply is sufficiently
*	faster than the regular modular multiplication, then the cost of
*	the two extra multiplies will be amortised over the loop and you
*	will probably end up being better off than using the regular
*	modular multiply. That might not be true for small exponents, but
*	for longer exponents (a typical cryptography exponent is 1024 bits,
*	that is, we're talking about ~1500 multiplications) the avoiding
*	of the software division used by the regular modular multiply
*	makes the Montgomery multiply a superior choice.
*
*	Now, the issue is, how to do Montgomery multiplication.
*	The algorithm to calculate it is simple. Here x[n] means the n-th
*	least significant word of x; in little-endian word order it actually
*	is what it is in C. On the other hand, x on its own means the whole
*	multi-word number. The variable tmp is a 32-bit unsigned int.
*
*	res = 0;
*
*	for ( i = 0 ; i < LEN( bop ) ; i++ ) {
*
*		tmp = (unsigned) ( ( res[ 0 ] + bop[ i ] * aop[ 0 ] ) * rem );
*		res = ( res + aop * bop[ i ] + mod * tmp ) >> 32
*	}
*
*	if ( res >= mod ) res -= mod;
*
*	The math details of the above algorithm can be found in the Handbook
*	of Applied Cryptography (among other textbooks and articles, but that
*	particular book is available on the Net).
*/

static int	_bc_NanuMont( BC_NANU nanu, int aop, int bop, int mod, int inv )
{
int		res, len;

	len = _bc_NanuVarLen( nanu, mod );
	if ( ( res = _bc_NanuVarNew( nanu, len ) ) >= 0 ) {

		NanuMont( _bc_NanuVarPtr( nanu, res ),
				  _bc_NanuVarPtr( nanu, aop ),
				  _bc_NanuVarPtr( nanu, bop ),
				  _bc_NanuVarPtr( nanu, mod ),
				  _bc_NanuVarLen( nanu, aop ),
				  _bc_NanuVarLen( nanu, bop ),
				  len, inv );

		_bc_NanuVarNrm( nanu, res );
	}

	return( res );
}

/*
*	The actual core of the MOntgomery multiply
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static void	NanuMont( unsigned *res, unsigned *aop, unsigned *bop,
					  unsigned *mod, unsigned aln,  unsigned bln,
					  unsigned mln,  unsigned rem )
{
unsigned	tmp, cnt, acc, bxi;

	// Clear the result

	_bc_NanuZero( res, mln );

	// Here we do the full monty :-)
	// For the multiplication, we use a special assembly function

	for ( acc = cnt = 0 ; cnt < mln ; cnt++ ) {

		bxi = cnt < bln ? bop[ cnt ] : 0;
		tmp = ( res[ 0 ] + bxi * aop[ 0 ] ) * rem;
		acc = _bc_NanuMulm( res, aop, mod, tmp, bxi, acc, aln, mln );
	}

	if ( ! acc ) {

		// We have to compare the modulus and the result.

		for ( aop = mod + mln, bop = res + mln, cnt = mln + 1 ; --cnt ; ) {

			tmp = *--aop;
			acc = *--bop;

			if ( tmp > acc ) {

				// The modulus is larger than the result, we're done

				return;
			}

			if ( tmp < acc ) {

				// The modulus is smaller than the result

				break;
			}
		}

		// NOTE: if !cnt then the final result is 0, if we have fast fill,
		// it might be faster. We'll see.
	}

	// We're here if the result is not less than the
	// modulus. Subtract the modulus from the result.

	_bc_NanuSub2( res, res, mod, mln );
}

