/*************************************************
* IF Scheme Source File                          *
* (C) 1999-2003 The Botan Project                *
*************************************************/

#include <botan/if_algo.h>
#include <botan/numthry.h>
#include <botan/asn1.h>

namespace Botan {

/*************************************************
* IF_Scheme_PublicKey Initializer                *
*************************************************/
void IF_Scheme_PublicKey::if_initialize(const BigInt& mod, const BigInt& exp,
                                        bool odd_exp)
   {
   if(mod <= 15 || mod % 2 == 0)
      throw Invalid_Argument(algo_name() + ": Invalid public key");
   if(exp <= 1 || (odd_exp && (exp % 2 == 0)) || (!odd_exp && (exp % 2 == 1)))
      throw Invalid_Argument(algo_name() + ": Invalid public key");
   n = mod;
   e = exp;
   powermod_e_n = FixedExponent_Exp(e, n);
   }

/*************************************************
* Return the X.509 public key encoding           *
*************************************************/
SecureVector<byte> IF_Scheme_PublicKey::DER_encode_pub() const
   {
   DER_Encoder encoder;
   encoder.start_sequence();
   DER::encode(encoder, n);
   DER::encode(encoder, e);
   encoder.end_sequence();
   return encoder.get_contents();
   }

/*************************************************
* Decode a X.509 public key encoding             *
*************************************************/
void IF_Scheme_PublicKey::BER_decode_pub(DataSource& source)
   {
   BER_Decoder decoder(source);
   BER_Decoder sequence = BER::get_subsequence(decoder);
   BigInt mod, exp;
   BER::decode(sequence, mod);
   BER::decode(sequence, exp);
   sequence.verify_end();

   if(algo_name() == "RSA")     if_initialize(mod, exp, true);
   else if(algo_name() == "RW") if_initialize(mod, exp, false);
   else
      throw Internal_Error("IF_Scheme_PublicKey: Unknown type " + algo_name());
   }

/*************************************************
* IF Scheme Private Key Operation                *
*************************************************/
BigInt IF_Scheme_PrivateKey::if_private_op(const BigInt& i) const
   {
   if(i >= n || i.is_negative())
      throw Invalid_Argument(algo_name() + "::private_op: i >= n || i < 0");

   BigInt ic = blinder.blind(i);
   BigInt j1 = powermod_d1_p(ic);
   BigInt j2 = powermod_d2_q(ic);
   BigInt h = powermod_d1_p.reduce(sub_mul(j1, j2, c));
   return blinder.unblind(mul_add(h, q, j2));
   }

/*************************************************
* IF Scheme Precomputation                       *
*************************************************/
void IF_Scheme_PrivateKey::precompute()
   {
   if(n == 0)
      throw Internal_Error(algo_name() + ": precompute() with modulus = 0");
   d1 = d % (p - 1);
   d2 = d % (q - 1);
   c = inverse_mod(q, p);

   setup_blinder();
   }

/*************************************************
* Set up the blinder and exponentiators          *
*************************************************/
void IF_Scheme_PrivateKey::setup_blinder()
   {
   powermod_d1_p = FixedExponent_Exp(d1, p);
   powermod_d2_q = FixedExponent_Exp(d2, q);
   if(!powermod_e_n.initialized())
      powermod_e_n = FixedExponent_Exp(e, n);

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

/*************************************************
* Check IF Scheme Private Parameters             *
*************************************************/
bool IF_Scheme_PrivateKey::check_params() const
   {
   if(p * q != n)
      return false;
   if(c != inverse_mod(q, p))
      return false;
   if(!check_prime(p) || !check_prime(q))
      return false;
   return true;
   }

/*************************************************
* Return the PKCS #1 private key encoding        *
*************************************************/
SecureVector<byte> IF_Scheme_PrivateKey::DER_encode_priv() const
   {
   DER_Encoder encoder;
   encoder.start_sequence();
   DER::encode(encoder, BigInt::zero());
   DER::encode(encoder, n);
   DER::encode(encoder, e);
   DER::encode(encoder, d);
   DER::encode(encoder, p);
   DER::encode(encoder, q);
   DER::encode(encoder, d1);
   DER::encode(encoder, d2);
   DER::encode(encoder, c);
   encoder.end_sequence();
   return encoder.get_contents();
   }

/*************************************************
* Decode a PKCS #1 private key encoding          *
*************************************************/
void IF_Scheme_PrivateKey::BER_decode_priv(DataSource& source)
   {
   BER_Decoder decoder(source);
   BER_Decoder sequence = BER::get_subsequence(decoder);
   BigInt version;
   BER::decode(sequence, version);
   if(version != 0)
      throw Decoding_Error(algo_name() + ": Unknown PKCS #1 key version");
   BER::decode(sequence, n);
   BER::decode(sequence, e);
   BER::decode(sequence, d);
   BER::decode(sequence, p);
   BER::decode(sequence, q);
   BER::decode(sequence, d1);
   BER::decode(sequence, d2);
   BER::decode(sequence, c);
   sequence.verify_end();

   if(n < 15 || e < 2 || d < 2 || p < 3 || p >= n || q < 3 || q >= n)
      throw Invalid_Argument(algo_name() + ": Invalid private key");

   setup_blinder();
   }

}
