Milán Major

netscape-revival

1 branch
Code

security/lib/crypto/cmpmod.c

/* Copyright (C) RSA Data Security, Inc. created 1995.  This is an
   unpublished work protected as such under copyright law.  This work
   contains proprietary, confidential, and trade secret information of
   RSA Data Security, Inc.  Use, disclosure or reproduction without the
   express written authorization of RSA Data Security, Inc. is
   prohibited.
 */

/* This file contains routines that perform modular arithmatic. Modular
     reduction resides in the multiply divide file because it is based
     on and uses the local support routines of divide.
 */
#include "cmp.h"
#include "cmppriv.h"
#include "cmpspprt.h"

/* sum = (addend1 + addend2) mod modulus
 */
CMPStatus
CMP_ModAdd(CMPInt *addend1, CMPInt *addend2, CMPInt *modulus, CMPInt *sum)
{
  int status;
  CMPInt reducedSum;

#ifdef CMP_DEBUG
  if (CMP_PINT_VALUE (addend1) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (addend1) <= 0)
    return (CMP_LENGTH);
  if (CMP_PINT_VALUE (addend2) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (addend2) <= 0)
    return (CMP_LENGTH);
#endif

  CMP_Constructor (&reducedSum);

  do {
    if ((status = CMP_Add (addend1, addend2, sum)) != 0)
      break;

    /* If the result of the addition is < modulus, done.
     */
    if (CMP_Compare (sum, modulus) < 0)
      break;

    /* Otherwise, reduce.
     */
    if ((status = CMP_SubtractInPlace (modulus, sum)) != 0)
      break;

    /* One subtraction was probably all it took ... */
    if (CMP_Compare (sum, modulus) < 0)
      break;

    /* ... if not, though, reduce. */
    if ((status = CMP_ModularReduce (sum, modulus, &reducedSum)) != 0)
      break;
    status = CMP_Move (&reducedSum, sum);
  } while (0);

  CMP_Destructor (&reducedSum);

  return (status);
}

/* Modular subtraction routine.
     if (minuend >= subtrahend) then
       difference = (minuend - subtrahend) mod modulus
     but if (subtrahend > minuend
       difference = modulus - [ (subtrahend - minuend) mod modulus ]
 */
CMPStatus
CMP_ModSubtract(CMPInt *minuend, CMPInt *subtrahend, CMPInt *modulus,
		CMPInt *difference)
{
  int status;
  CMPInt reducedDifference;

#ifdef CMP_DEBUG
  if (CMP_PINT_VALUE (minuend) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (minuend) <= 0)
    return (CMP_LENGTH);
  if (CMP_PINT_VALUE (subtrahend) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (subtrahend) <= 0)
    return (CMP_LENGTH);
  if (CMP_PINT_VALUE (modulus) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (modulus) <= 0)
    return (CMP_LENGTH);
#endif

  CMP_Constructor (&reducedDifference);

  do {
    if (CMP_Compare (minuend, subtrahend) >= 0) {
      /* If the minuend >= subtrahend, do regular subtraction followed by
           modular reduction if necessary.
       */
      if ((status = CMP_Subtract
           (minuend, subtrahend, difference)) != 0)
        break;

      /* If the result of that subtraction is < modulus, done.
       */
      if (CMP_Compare (difference, modulus) < 0)
        break;

      /* Otherwise, reduce.
       */
      if ((status = CMP_ModularReduce
           (difference, modulus, &reducedDifference)) != 0)
        break;

      status = CMP_Move (&reducedDifference, difference);
      break;
    }

    /* subtrahend > minuend, first find subtrahend - minuend.
     */
    if ((status = CMP_Subtract
         (subtrahend, minuend, &reducedDifference)) != 0)
      break;

    /* If the difference <= modulus, the answer is modulus - difference.
     */
    if (CMP_Compare (&reducedDifference, modulus) <= 0) {
      status = CMP_Subtract (modulus, &reducedDifference, difference);
    }

    /* (subtrahend - minuend) > modulus, find
          answer = modulus - [(subtrahend - minuend) mod modulus]
     */
    else {
      if ((status = CMP_Move (&reducedDifference, difference)) != 0)
        break;
      if ((status = CMP_ModularReduce
           (difference, modulus, &reducedDifference)) != 0)
        break;

      /* If the original subtrahend = minuend + (k * modulus), at this
           point, reducedDifference = 0 and the result should be 0. We
           know that difference has space of at least 1.
       */
      if ( (CMP_INT_LENGTH (reducedDifference) == 1) &&
           (CMP_INT_WORD (reducedDifference, 0) == (CMPWord)0) ) {
        CMP_PINT_LENGTH (difference) = 1;
        CMP_PINT_WORD (difference, 0) = (CMPWord)0;
      }
      else {
        status = CMP_Subtract
          (modulus, &reducedDifference, difference);
      }
    }
  } while (0);

  CMP_Destructor (&reducedDifference);

  return (status);
}

/*  product = (multiplicand * multiplier) mod modulus
 */
CMPStatus
CMP_ModMultiply(CMPInt *multiplicand, CMPInt *multiplier, CMPInt *modulus,
		CMPInt *product)
{
  int status;
  CMPInt intermediateProduct;

#ifdef CMP_DEBUG
  if (CMP_PINT_VALUE (multiplicand) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (multiplicand) <= 0)
    return (CMP_LENGTH);
  if (CMP_PINT_VALUE (multiplier) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (multiplier) <= 0)
    return (CMP_LENGTH);
  if (CMP_PINT_VALUE (modulus) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (modulus) <= 0)
    return (CMP_LENGTH);
#endif

  CMP_Constructor (&intermediateProduct);

  do {
    if ((status = CMP_Multiply
         (multiplicand, multiplier, &intermediateProduct)) != 0)
      break;

    status = CMP_ModularReduce (&intermediateProduct, modulus, product);
  } while (0);

  CMP_Destructor (&intermediateProduct);

  return(status);
}

/* Computes modular inverse.
*/
CMPStatus
CMP_ModInvert(CMPInt *operand, CMPInt *modulus, CMPInt *inverse)
{
  int status;
  CMPInt coeff2, gcd;

#ifdef CMP_DEBUG
  if (CMP_PINT_VALUE (operand) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (operand) <= 0)
    return (CMP_LENGTH);
  if (CMP_PINT_VALUE (modulus) == CMP_NULL_VALUE)
    return (CMP_INVALID_ADDRESS);
  if (CMP_PINT_LENGTH (modulus) <= 0)
    return (CMP_LENGTH);
#endif

  CMP_Constructor (&coeff2);
  CMP_Constructor (&gcd);

  do {
    /* If the operand >= modulus, error. */
    if (CMP_Compare (operand, modulus) >= 0) {
      if (CMP_PINT_WORD (modulus, CMP_PINT_LENGTH (modulus) - 1) == (CMPWord)0)
        status = CMP_MODULUS;
      else
        status = CMP_RANGE;
      break;
    }

    if ((status = CMP_ComputeExtendedGCD
         (operand, modulus, inverse, &coeff2, &gcd)) != 0)
      break;

    if ( (CMP_INT_LENGTH (gcd) != 1) ||
         (CMP_INT_WORD (gcd, 0) != (CMPWord)1) )
      status = CMP_INVERSE;

  } while (0);

  CMP_Destructor (&coeff2);
  CMP_Destructor (&gcd);

  return (status);
}