int aws_s_mp_exptmod()

in AWSCognitoIdentityProvider/Internal/JKBigInteger/LibTomMath/tommath.c [2816:3042]


int aws_s_mp_exptmod(aws_mp_int *G, aws_mp_int *X, aws_mp_int *P, aws_mp_int *Y, int redmode)
{
  aws_mp_int M[TAB_SIZE], res, mu;
  aws_mp_digit buf;
  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
  int (*redux)(aws_mp_int *, aws_mp_int *, aws_mp_int *);

  /* find window size */
  x = aws_mp_count_bits(X);
  if (x <= 7) {
    winsize = 2;
  } else if (x <= 36) {
    winsize = 3;
  } else if (x <= 140) {
    winsize = 4;
  } else if (x <= 450) {
    winsize = 5;
  } else if (x <= 1303) {
    winsize = 6;
  } else if (x <= 3529) {
    winsize = 7;
  } else {
    winsize = 8;
  }

#ifdef AWS_MP_LOW_MEM
    if (winsize > 5) {
       winsize = 5;
    }
#endif

  /* init M array */
  /* init first cell */
  if ((err = aws_mp_init(&M[1])) != AWS_MP_OKAY) {
     return err; 
  }

  /* now init the second half of the array */
  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
    if ((err = aws_mp_init(&M[x])) != AWS_MP_OKAY) {
      for (y = 1<<(winsize-1); y < x; y++) {
          aws_mp_clear(&M[y]);
      }
        aws_mp_clear(&M[1]);
      return err;
    }
  }

  /* create mu, used for Barrett reduction */
  if ((err = aws_mp_init(&mu)) != AWS_MP_OKAY) {
    goto LBL_M;
  }
  
  if (redmode == 0) {
     if ((err = aws_mp_reduce_setup(&mu, P)) != AWS_MP_OKAY) {
        goto LBL_MU;
     }
     redux = aws_mp_reduce;
  } else {
     if ((err = aws_mp_reduce_2k_setup_l(P, &mu)) != AWS_MP_OKAY) {
        goto LBL_MU;
     }
     redux = aws_mp_reduce_2k_l;
  }    

  /* create M table
   *
   * The M table contains powers of the base, 
   * e.g. M[x] = G**x mod P
   *
   * The first half of the table is not 
   * computed though accept for M[0] and M[1]
   */
  if ((err = aws_mp_mod(G, P, &M[1])) != AWS_MP_OKAY) {
    goto LBL_MU;
  }

  /* compute the value at M[1<<(winsize-1)] by squaring 
   * M[1] (winsize-1) times 
   */
  if ((err = aws_mp_copy(&M[1], &M[1 << (winsize - 1)])) != AWS_MP_OKAY) {
    goto LBL_MU;
  }

  for (x = 0; x < (winsize - 1); x++) {
    /* square it */
    if ((err = aws_mp_sqr(&M[1 << (winsize - 1)],
            &M[1 << (winsize - 1)])) != AWS_MP_OKAY) {
      goto LBL_MU;
    }

    /* reduce modulo P */
    if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != AWS_MP_OKAY) {
      goto LBL_MU;
    }
  }

  /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
   * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
   */
  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
    if ((err = aws_mp_mul(&M[x - 1], &M[1], &M[x])) != AWS_MP_OKAY) {
      goto LBL_MU;
    }
    if ((err = redux (&M[x], P, &mu)) != AWS_MP_OKAY) {
      goto LBL_MU;
    }
  }

  /* setup result */
  if ((err = aws_mp_init(&res)) != AWS_MP_OKAY) {
    goto LBL_MU;
  }
    aws_mp_set(&res, 1);

  /* set initial mode and bit cnt */
  mode   = 0;
  bitcnt = 1;
  buf    = 0;
  digidx = X->used - 1;
  bitcpy = 0;
  bitbuf = 0;

  for (;;) {
    /* grab next digit as required */
    if (--bitcnt == 0) {
      /* if digidx == -1 we are out of digits */
      if (digidx == -1) {
        break;
      }
      /* read next digit and reset the bitcnt */
      buf    = X->dp[digidx--];
      bitcnt = (int) AWS_DIGIT_BIT;
    }

    /* grab the next msb from the exponent */
    y     = (buf >> (aws_mp_digit)(AWS_DIGIT_BIT - 1)) & 1;
    buf <<= (aws_mp_digit)1;

    /* if the bit is zero and mode == 0 then we ignore it
     * These represent the leading zero bits before the first 1 bit
     * in the exponent.  Technically this opt is not required but it
     * does lower the # of trivial squaring/reductions used
     */
    if (mode == 0 && y == 0) {
      continue;
    }

    /* if the bit is zero and mode == 1 then we square */
    if (mode == 1 && y == 0) {
      if ((err = aws_mp_sqr(&res, &res)) != AWS_MP_OKAY) {
        goto LBL_RES;
      }
      if ((err = redux (&res, P, &mu)) != AWS_MP_OKAY) {
        goto LBL_RES;
      }
      continue;
    }

    /* else we add it to the window */
    bitbuf |= (y << (winsize - ++bitcpy));
    mode    = 2;

    if (bitcpy == winsize) {
      /* ok window is filled so square as required and multiply  */
      /* square first */
      for (x = 0; x < winsize; x++) {
        if ((err = aws_mp_sqr(&res, &res)) != AWS_MP_OKAY) {
          goto LBL_RES;
        }
        if ((err = redux (&res, P, &mu)) != AWS_MP_OKAY) {
          goto LBL_RES;
        }
      }

      /* then multiply */
      if ((err = aws_mp_mul(&res, &M[bitbuf], &res)) != AWS_MP_OKAY) {
        goto LBL_RES;
      }
      if ((err = redux (&res, P, &mu)) != AWS_MP_OKAY) {
        goto LBL_RES;
      }

      /* empty window and reset */
      bitcpy = 0;
      bitbuf = 0;
      mode   = 1;
    }
  }

  /* if bits remain then square/multiply */
  if (mode == 2 && bitcpy > 0) {
    /* square then multiply if the bit is set */
    for (x = 0; x < bitcpy; x++) {
      if ((err = aws_mp_sqr(&res, &res)) != AWS_MP_OKAY) {
        goto LBL_RES;
      }
      if ((err = redux (&res, P, &mu)) != AWS_MP_OKAY) {
        goto LBL_RES;
      }

      bitbuf <<= 1;
      if ((bitbuf & (1 << winsize)) != 0) {
        /* then multiply */
        if ((err = aws_mp_mul(&res, &M[1], &res)) != AWS_MP_OKAY) {
          goto LBL_RES;
        }
        if ((err = redux (&res, P, &mu)) != AWS_MP_OKAY) {
          goto LBL_RES;
        }
      }
    }
  }

    aws_mp_exch(&res, Y);
  err = AWS_MP_OKAY;
LBL_RES:
aws_mp_clear(&res);
LBL_MU:
aws_mp_clear(&mu);
LBL_M:
aws_mp_clear(&M[1]);
  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
      aws_mp_clear(&M[x]);
  }
  return err;
}