/*

  pollard.c

  Author: Mika Kojo <mkojo@ssh.fi>

  Copyright (c) 1999 SSH Communications Security, Finland
  All rights reserved.

  Created Fri Jan 14 21:11:09 2000.

  */

/* Some simple factorization methods due to Pollard. */


#include "sshincludes.h"
#include "sshmp.h"
#include "pollard.h"

/* This Pollard rho implementation is written to follow the description given
   by Cohen in CCANT. */
Boolean ssh_pollard_rho(SshInt *factor, const SshInt *composite,
                        SshWord steps, SshWord coefficient)
{
  SshInt x, x1, y, p, c, g;
  SshIntModQ xp, x1p, yp, cp, pp, tp;
  SshIntModuli m;
  Boolean rv = FALSE;
  SshWord counter, accumulate, k, l;

  /* Initialize the needed variables. */
  ssh_mp_init(&x);
  ssh_mp_init(&x1);
  ssh_mp_init(&y);
  ssh_mp_init(&p);
  ssh_mp_init(&c);
  ssh_mp_init(&g);

  /* Define the Montgomery representation stuff. */
  ssh_mpm_init_m(&m, composite);

  ssh_mpm_init(&xp, &m);
  ssh_mpm_init(&x1p, &m);
  ssh_mpm_init(&yp, &m);
  ssh_mpm_init(&cp, &m);
  ssh_mpm_init(&pp, &m);
  ssh_mpm_init(&tp, &m);

  /* Setup the values. */
  accumulate = 0;
  ssh_mp_set_ui(&y,  2);
  ssh_mp_set_ui(&x,  2);
  ssh_mp_set_ui(&x1, 2);
  ssh_mp_set_ui(&p,  1);
  ssh_mp_set_ui(&c,  coefficient);
  k = 1;
  l = 1;
  
  /* Set the Monty. */
  ssh_mpm_set_mp(&yp,  &y);
  ssh_mpm_set_mp(&xp,  &x);
  ssh_mpm_set_mp(&x1p, &x1);
  ssh_mpm_set_mp(&cp,  &c);
  ssh_mpm_set_mp(&pp,  &p);
  ssh_mpm_set_mp(&tp,  &x);
  
  for (counter = 0; counter < steps; counter++)
    {
      /* Compute: x = x^2 + c (mod n) */
      ssh_mpm_square(&xp, &xp);
      ssh_mpm_add(&xp, &xp, &cp);

      /* Now accumulate. */
      ssh_mpm_sub(&tp, &x1p, &xp);
      ssh_mpm_mul(&pp, &pp, &tp);

      accumulate++;
      /* Accumulate until suitable bound has been reached. */
      if (accumulate > 200)
        {
          /* Now check the GCD. */
          ssh_mp_set_mpm(&p, &pp);
          ssh_mp_gcd(&g, &p, composite);
          if (ssh_mp_cmp_ui(&g, 1) > 0)
            {
            backtrack:
              if (ssh_mp_cmp(&g, composite) == 0)
                goto finished;
              
              /* Backtrack. */
              ssh_mp_set_mpm(&y, &yp);
              ssh_mp_set_mpm(&x1, &x1p);
              ssh_mp_set_mpm(&c, &cp);
              
              while (1)
                {
                  /* Compute y = y^2 + c (mod n) */
                  ssh_mp_square(&y, &y);
                  ssh_mp_add(&y, &y, &c);
                  ssh_mp_mod(&y, &y, composite);

                  /* Compute gcd(x1 - y, n) */
                  ssh_mp_sub(&p, &x1, &y);
                  ssh_mp_gcd(&g, &p, composite);

                  /* Check the gcd, this must happen. */
                  if (ssh_mp_cmp_ui(&g, 1) > 0)
                    {
                      if (ssh_mp_cmp(&g, composite) == 0)
                        goto finished;

                      ssh_mp_set(factor, &g);
                      rv = TRUE;
                      goto finished;
                    }
                }
            }
          ssh_mpm_set(&yp, &xp);
          accumulate = 0;
        }

      k--;
      if (k == 0)
        {
          SshWord i;
          
          ssh_mp_set_mpm(&p, &pp);
          ssh_mp_gcd(&g, &p, composite);
          if (ssh_mp_cmp_ui(&g, 1) > 0)
            goto backtrack;

          ssh_mpm_set(&x1p, &xp);

          k  = l;
          l *= 2;
          
          for (i = 0; i < k; i++)
            {
              ssh_mpm_square(&xp, &xp);
              ssh_mpm_add(&xp, &xp, &cp);
            }
          ssh_mpm_set(&yp, &xp);
          accumulate = 0;
        }
    }
finished:
  
  /* Clear the variables. */
  ssh_mp_clear(&x);
  ssh_mp_clear(&x1);
  ssh_mp_clear(&y);
  ssh_mp_clear(&p);
  ssh_mp_clear(&c);
  ssh_mp_clear(&g);

  /* Clear the Montgomery stuff. */
  ssh_mpm_clear_m(&m);
  ssh_mpm_clear(&xp);
  ssh_mpm_clear(&x1p);
  ssh_mpm_clear(&yp);
  ssh_mpm_clear(&cp);
  ssh_mpm_clear(&pp);
  ssh_mpm_clear(&tp);
  
  return rv;
}


/* pollard.c */
