/*************************************************
* Nyberg-Rueppel Source File                     *
* (C) 1999-2003 The Botan Project                *
*************************************************/

#include <botan/nr.h>
#include <botan/numthry.h>
#include <botan/keypair.h>

namespace Botan {

/*************************************************
* NR_PublicKey Constructor                       *
*************************************************/
NR_PublicKey::NR_PublicKey(const DL_Group& domain, const BigInt& key)
   {
   dl_initialize(domain, key);
   powermod_g_p = FixedBase_Exp(group.get_g(), group.get_p());
   powermod_y_p = FixedBase_Exp(y, group.get_p());
   }

/*************************************************
* Nyberg-Rueppel Verification Function           *
*************************************************/
SecureVector<byte> NR_PublicKey::verify(const byte sig[], u32bit sig_len) const
   {
   const BigInt& q = group.get_q();
   const BigInt& p = group.get_p();
   if(sig_len != 2*q.bytes())
      return false;

   BigInt c(sig, q.bytes());
   BigInt d(sig + q.bytes(), q.bytes());

   if(c.is_zero() || c >= q || d >= q)
      throw Invalid_Argument(algo_name() + ": Invalid signature");

   BigInt i = (powermod_g_p(d) * powermod_y_p(c)) % p;
   BigInt f = (c - i) % q;

   return BigInt::encode(f);
   }

/*************************************************
* Decode X.509 NR parameters                     *
*************************************************/
void NR_PublicKey::BER_decode_params(DataSource& source)
   {
   group.BER_decode(source, DL_Group::ANSI_X9_57);
   }

/*************************************************
* Return the parameters in X.509 encoding        *
*************************************************/
SecureVector<byte> NR_PublicKey::DER_encode_params() const
   {
   return group.DER_encode(DL_Group::ANSI_X9_57);
   }

/*************************************************
* Decode a X.509 public key encoding             *
*************************************************/
void NR_PublicKey::BER_decode_pub(DataSource& source)
   {
   DL_Scheme_PublicKey::BER_decode_pub(source);
   powermod_g_p = FixedBase_Exp(group.get_g(), group.get_p());
   powermod_y_p = FixedBase_Exp(y, group.get_p());
   }

/*************************************************
* Return the name of this algorithm              *
*************************************************/
std::string NR_PublicKey::algo_name() const
   {
   return "NR";
   }

/*************************************************
* Return the maximum input size in bits          *
*************************************************/
u32bit NR_PublicKey::max_input_bits() const
   {
   return (group.get_q().bits() - 1);
   }

/*************************************************
* Return the size of each portion of the sig     *
*************************************************/
u32bit NR_PublicKey::message_part_size() const
   {
   const BigInt& q = group.get_q();
   return q.bytes();
   }

/*************************************************
* NR_PrivateKey Constructor                      *
*************************************************/
NR_PrivateKey::NR_PrivateKey(const DL_Group& domain) : NR_PublicKey(domain, 0)
   {
   x = random_integer(1, group.get_q() - 1, LongTermKey);
   y = powermod_g_p(x);
   powermod_y_p = FixedBase_Exp(y, group.get_p());

   if(!check_params())
      throw Self_Test_Failure(algo_name() + " private key generation failed");
   }

/*************************************************
* NR_PrivateKey Constructor                      *
*************************************************/
NR_PrivateKey::NR_PrivateKey(const DL_Group& domain,
                             const BigInt& priv, const BigInt& pub) :
   NR_PublicKey(domain, pub)
   {
   x = priv;
   if(x <= 1 || x >= group.get_q())
      throw Invalid_Argument(algo_name() + ": Invalid private key");
   }

/*************************************************
* Nyberg-Rueppel Signature Operation             *
*************************************************/
SecureVector<byte> NR_PrivateKey::sign(const byte in[], u32bit len) const
   {
   const BigInt& q = group.get_q();
   BigInt f;
   f.binary_decode(in, len);

   if(f >= q || f.is_negative())
      throw Invalid_Argument("NR_PrivateKey::sign: Input is too large");

   while(true)
      {
      BigInt k;
      do
         k.randomize(q.bits(), SessionKey);
      while(k >= q);

      BigInt c = (powermod_g_p(k) + f) % q;
      if(c.is_zero())
         continue;

      BigInt d = (k - x * c) % q;

      SecureVector<byte> output(2*q.bytes());
      c.binary_encode(output + (output.size() / 2 - c.bytes()));
      d.binary_encode(output + (output.size() - d.bytes()));
      return output;
      }
   }

/*************************************************
* Decode a PKCS #8 NR private key                *
*************************************************/
void NR_PrivateKey::BER_decode_priv(DataSource& source)
   {
   DL_Scheme_PrivateKey::BER_decode_priv(source);
   powermod_g_p = FixedBase_Exp(group.get_g(), group.get_p());
   y = powermod_g_p(x);
   powermod_y_p = FixedBase_Exp(y, group.get_p());
   }

/*************************************************
* Check Private Nyberg-Rueppel Parameters        *
*************************************************/
bool NR_PrivateKey::check_params() const
   {
   if(!DL_Scheme_PublicKey::check_params())
      return false;
   if(y != powermod_g_p(x))
      return false;

   try {
      KeyPair::check_key(get_pk_signer(*this, "EMSA1(SHA-1)"),
                         get_pk_verifier(*this, "EMSA1(SHA-1)")
         );
      }
   catch(Self_Test_Failure)
      {
      return false;
      }

   return true;
   }

}
