/*************************************************
* Diffie-Hellman Source File                     *
* (C) 1999-2003 The Botan Project                *
*************************************************/

#include <botan/dh.h>
#include <botan/numthry.h>

namespace Botan {

/*************************************************
* DH_PublicKey Constructor                       *
*************************************************/
DH_PublicKey::DH_PublicKey(const DL_Group& domain, const BigInt& key)
   {
   dl_initialize(domain, key);
   }

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

/*************************************************
* Decode X.509 DH parameters                     *
*************************************************/
void DH_PublicKey::BER_decode_params(DataSource& source)
   {
   group.BER_decode(source, DL_Group::ANSI_X9_42);
   }

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

/*************************************************
* Return the public value for key agreement      *
*************************************************/
SecureVector<byte> DH_PublicKey::public_value() const
   {
   const BigInt& p = group.get_p();
   return BigInt::encode_1363(y, p.bytes());
   }

/*************************************************
* DH_PrivateKey Constructor                      *
*************************************************/
DH_PrivateKey::DH_PrivateKey(const DL_Group& domain) : DH_PublicKey(domain, 0)
   {
   const BigInt& p = group.get_p();
   const BigInt& g = group.get_g();
   x = random_integer(2 * dl_work_factor(p.bits()), LongTermKey);
   powermod_x_p = FixedExponent_Exp(x, p);
   y = powermod_x_p(g);

   BigInt k = random_integer(p.bits() - 1, SessionKey);
   blinder.initialize(k, powermod_x_p(inverse_mod(k, p)), p);
   }

/*************************************************
* DH_PrivateKey Constructor                      *
*************************************************/
DH_PrivateKey::DH_PrivateKey(const DL_Group& domain,
                             const BigInt& priv_key, const BigInt& pub_key) :
   DH_PublicKey(domain, pub_key)
   {
   x = priv_key;
   powermod_x_p = FixedExponent_Exp(x, group.get_p());
   const BigInt& p = group.get_p();

   if(x <= 1 || y <= 1 || x >= p || y >= p)
      throw Invalid_Argument(algo_name() + ": invalid private key");

   BigInt k = random_integer(p.bits() - 1, SessionKey);
   blinder.initialize(k, powermod_x_p(inverse_mod(k, p)), p);
   }

/*************************************************
* Return the public value for key agreement      *
*************************************************/
SecureVector<byte> DH_PrivateKey::public_value() const
   {
   return DH_PublicKey::public_value();
   }

/*************************************************
* Decode a PKCS #8 DH private key                *
*************************************************/
void DH_PrivateKey::BER_decode_priv(DataSource& source)
   {
   DL_Scheme_PrivateKey::BER_decode_priv(source);

   const BigInt& p = group.get_p();
   powermod_x_p = FixedExponent_Exp(x, p);
   y = powermod_x_p(group.get_g());

   BigInt k = random_integer(p.bits() - 1, SessionKey);
   blinder.initialize(k, powermod_x_p(inverse_mod(k, p)), p);
   }

/*************************************************
* Check Private DH Parameters                    *
*************************************************/
bool DH_PrivateKey::check_params() const
   {
   if(!DL_Scheme_PublicKey::check_params())
      return false;
   if(x < 1 || x >= group.get_p())
      return false;
   if(y != powermod_x_p(group.get_g()))
      return false;
   return true;
   }

/*************************************************
* Derive a key                                   *
*************************************************/
SecureVector<byte> DH_PrivateKey::derive_key(const byte w[],
                                             u32bit w_len) const
   {
   return derive_key(BigInt::decode(w, w_len));
   }

/*************************************************
* Derive a key                                   *
*************************************************/
SecureVector<byte> DH_PrivateKey::derive_key(const DH_PublicKey& key) const
   {
   return derive_key(key.get_y());
   }

/*************************************************
* Derive a key                                   *
*************************************************/
SecureVector<byte> DH_PrivateKey::derive_key(const BigInt& w) const
   {
   const BigInt& p = group.get_p();
   if(w <= 1 || w >= p-1)
      throw Invalid_Argument(algo_name() + "::derive_key: Invalid key input");

   BigInt z = blinder.unblind(powermod_x_p(blinder.blind(w)));
   return BigInt::encode_1363(z, p.bytes());
   }

}
