#include "crypt.h"
#include "bigint.h"
#include "sha1.h"
#include "schnorr.h"

static const u_int HASHSIZE = sha1::hashsize;

/* bind_r_to_m is just a fancy name for a procedure that
   hashes a message m and a random element of the subgroup
   of Z_p^* generated by g into a 160-bit SHA-1 hash */
void 
schnorr_pub::bind_r_to_m (bigint *e, const str &m, const bigint &r) const
{
  sha1ctx sc;
  sc.update (m.cstr (), m.len ());
  
  str r_as_str = r.getraw ();
  sc.update (r_as_str.cstr (), r_as_str.len ());
  
  char m_r_hashed[sha1::hashsize];
  sc.final (m_r_hashed);
  
  mpz_set_rawmag_le(e, m_r_hashed, sizeof (m_r_hashed));
}

/*
 * Straight-Ahead Schnorr:
 *
 *  s = (k * e^-1 + x) * e (mod q)
 */
bool
schnorr_priv::sign (bigint *r, bigint *s, const str &msg)
{
  assert (r && s);
  make_ekp ();
  if (!ekp)
    return false;
  bigint e;
  *r = ekp->public_half ();
  bind_r_to_m (&e, msg, *r);
  bigint t (invert (e, q));
  if (t < 0)
    t += q;
  t *= ekp->private_half ();
  t %= q;
  t += x;
  t *= e;
  *s = t % q;
  ekp = NULL;
  assert (check_signature (*r, *s, e, y)); // debug !!
  delaycb (0, wrap (this, &schnorr_priv::make_ekp));
  return true;
}

void
schnorr_priv::make_ekp ()
{
  if (ekp)
    return;
  ekp = make_ephem_key_pair ();
}


/* To thwart timing attacks (based on non-constant-time modular reduction
   implementation), we compute s_clnt as follows:

       s_clnt = ((k_clnt * (e^-1 mod q) + x_clnt) * e) mod q

   In this way the time will be spent almost entirely performing the two 
   modular multiplication, and in both case one factor is random, so that
   no information can be leaked about x_clnt */
bool 
schnorr_clnt_priv::complete_signature (bigint *r, bigint *s, 
				       const str &msg, 
				       const bigint &r_clnt, 
				       const bigint &k_clnt, 
				       const bigint &r_srv, 
				       const bigint &s_srv)
{
  assert (r && s);

  if (is_group_elem (r_srv)) {

    *r = (r_clnt * r_srv);
    *r %= p;

    bigint e;
    bind_r_to_m (&e, msg, *r);
  
    bigint s_clnt (invert (e, q));
    s_clnt *= k_clnt;
    s_clnt %= q;
    s_clnt += x_clnt;
    s_clnt %= q;
    s_clnt *= e;
    s_clnt %= q;

    *s = (s_clnt + s_srv);
    *s %= q; 
    
    return check_signature (*r, *s, e, y);
  }
  else
    return false;
}

/* To thwart timing attacks (see the comment to the function 
   schnorr_clnt_priv::complete_unwrapped_signature), we compute s_srv
   as follows:

       s_srv = ((k_srv * (e^-1 mod q) + x_srv) * e) mod q

   In this way the time will be spent almost entirely performing the two 
   modular multiplication, and in both case one factor is random, so that
   no information can be leaked about x_srv */
bool
schnorr_srv_priv::endorse_signature (bigint *r_srv, bigint *s_srv,
				     const str &msg, 
				     const bigint &r_clnt)
{
  assert ((r_srv != NULL) && (s_srv != NULL));


  if (is_group_elem (r_clnt)) {

    // server's ephemeral public key, private key pair
    ref<ephem_key_pair> ekp_srv = make_ephem_key_pair ();
    *r_srv = ekp_srv->public_half ();

    // combine client's and server's ephemeral public keys
    bigint r (r_clnt * (*r_srv));
    r %= p;
    
    bigint e;
    bind_r_to_m (&e, msg, r);
    
    *s_srv  = invert (e, q);
    *s_srv *= ekp_srv->private_half ();
    *s_srv %= q;
    *s_srv += x_srv;
    *s_srv %= q;
    *s_srv *= e;
    *s_srv %= q;

    return true;
  }
  else
    return false;
}

void
schnorr_gen::use_qpg (const bigint &q, const bigint &p, const bigint &g)
{
  bigint y, x, x_c, x_s;

  x_c = random_zn (q);
  x_s = random_zn (q);
  x = x_c + x_s;
  x %= q;
  y = powm (g, x, p);

  csk = New refcounted<schnorr_clnt_priv> (p, q, g, y, x_c);
  ssk = New refcounted<schnorr_srv_priv>  (p, q, g, y, x_s);
  wsk = New refcounted<schnorr_priv>      (p, q, g, y, x);

}

ptr<schnorr_clnt_priv>
schnorr_clnt_priv::update (bigint *deltap) const
{
  bigint delta;
  if (deltap && (*deltap > 0))
    delta = *deltap;
  else {
    delta = random_zn (q);
    if (deltap)
      *deltap = delta;
  }
  bigint nx_c = x_clnt + q;
  nx_c -= delta;
  nx_c %= q;
  return New refcounted <schnorr_clnt_priv> (p, q, g, y, nx_c);
}

ptr<schnorr_srv_priv>
schnorr_srv_priv::update (const bigint &dlt) const
{
  bigint nx_c = x_srv + dlt;
  nx_c %= q;
  return New refcounted <schnorr_srv_priv> (p, q, g, y, nx_c);
}
