/*
* Public Key Interface
* (C) 1999-2010 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_PUBKEY_H_
#define BOTAN_PUBKEY_H_

#include <botan/pk_keys.h>
#include <botan/pk_ops_fwd.h>
#include <botan/symkey.h>
#include <string>

#if defined(BOTAN_HAS_SYSTEM_RNG)
  #include <botan/system_rng.h>
  #define BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS
#endif

namespace Botan {

class RandomNumberGenerator;

/**
* Public Key Encryptor
* This is the primary interface for public key encryption
*/
class BOTAN_PUBLIC_API(2,0) PK_Encryptor
   {
   public:

      /**
      * Encrypt a message.
      * @param in the message as a byte array
      * @param length the length of the above byte array
      * @param rng the random number source to use
      * @return encrypted message
      */
      std::vector<uint8_t> encrypt(const uint8_t in[], size_t length,
                                 RandomNumberGenerator& rng) const
         {
         return enc(in, length, rng);
         }

      /**
      * Encrypt a message.
      * @param in the message
      * @param rng the random number source to use
      * @return encrypted message
      */
      template<typename Alloc>
      std::vector<uint8_t> encrypt(const std::vector<uint8_t, Alloc>& in,
                                RandomNumberGenerator& rng) const
         {
         return enc(in.data(), in.size(), rng);
         }

      /**
      * Return the maximum allowed message size in bytes.
      * @return maximum message size in bytes
      */
      virtual size_t maximum_input_size() const = 0;

      /**
      * Return an upper bound on the ciphertext length
      */
      virtual size_t ciphertext_length(size_t ctext_len) const = 0;

      PK_Encryptor() = default;
      virtual ~PK_Encryptor() = default;

      PK_Encryptor(const PK_Encryptor&) = delete;
      PK_Encryptor& operator=(const PK_Encryptor&) = delete;

   private:
      virtual std::vector<uint8_t> enc(const uint8_t[], size_t,
                                    RandomNumberGenerator&) const = 0;
   };

/**
* Public Key Decryptor
*/
class BOTAN_PUBLIC_API(2,0) PK_Decryptor
   {
   public:
      /**
      * Decrypt a ciphertext, throwing an exception if the input
      * seems to be invalid (eg due to an accidental or malicious
      * error in the ciphertext).
      *
      * @param in the ciphertext as a byte array
      * @param length the length of the above byte array
      * @return decrypted message
      */
      secure_vector<uint8_t> decrypt(const uint8_t in[], size_t length) const;

      /**
      * Same as above, but taking a vector
      * @param in the ciphertext
      * @return decrypted message
      */
      template<typename Alloc>
      secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc>& in) const
         {
         return decrypt(in.data(), in.size());
         }

      /**
      * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
      * invalid padding) or is not the expected length, instead
      * returns a random string of the expected length. Use to avoid
      * oracle attacks, especially against PKCS #1 v1.5 decryption.
      */
      secure_vector<uint8_t>
      decrypt_or_random(const uint8_t in[],
                        size_t length,
                        size_t expected_pt_len,
                        RandomNumberGenerator& rng) const;

      /**
      * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
      * invalid padding) or is not the expected length, instead
      * returns a random string of the expected length. Use to avoid
      * oracle attacks, especially against PKCS #1 v1.5 decryption.
      *
      * Additionally checks (also in const time) that:
      *    contents[required_content_offsets[i]] == required_content_bytes[i]
      * for 0 <= i < required_contents
      *
      * Used for example in TLS, which encodes the client version in
      * the content bytes: if there is any timing variation the version
      * check can be used as an oracle to recover the key.
      */
      secure_vector<uint8_t>
      decrypt_or_random(const uint8_t in[],
                        size_t length,
                        size_t expected_pt_len,
                        RandomNumberGenerator& rng,
                        const uint8_t required_content_bytes[],
                        const uint8_t required_content_offsets[],
                        size_t required_contents) const;

      /**
      * Return an upper bound on the plaintext length for a particular
      * ciphertext input length
      */
      virtual size_t plaintext_length(size_t ctext_len) const = 0;

      PK_Decryptor() = default;
      virtual ~PK_Decryptor() = default;

      PK_Decryptor(const PK_Decryptor&) = delete;
      PK_Decryptor& operator=(const PK_Decryptor&) = delete;

   private:
      virtual secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
                                             const uint8_t in[], size_t in_len) const = 0;
   };

/**
* Public Key Signer. Use the sign_message() functions for small
* messages. Use multiple calls update() to process large messages and
* generate the signature by finally calling signature().
*/
class BOTAN_PUBLIC_API(2,0) PK_Signer final
   {
   public:

      /**
      * Construct a PK Signer.
      * @param key the key to use inside this signer
      * @param rng the random generator to use
      * @param emsa the EMSA to use
      * An example would be "EMSA1(SHA-224)".
      * @param format the signature format to use
      * @param provider the provider to use
      */
      PK_Signer(const Private_Key& key,
                RandomNumberGenerator& rng,
                const std::string& emsa,
                Signature_Format format = IEEE_1363,
                const std::string& provider = "");

#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
      /**
      * Construct a PK Signer.
      * @param key the key to use inside this signer
      * @param emsa the EMSA to use
      * An example would be "EMSA1(SHA-224)".
      * @param format the signature format to use
      */
      BOTAN_DEPRECATED("Use constructor taking a RNG object")
      PK_Signer(const Private_Key& key,
                const std::string& emsa,
                Signature_Format format = IEEE_1363,
                const std::string& provider = "") :
         PK_Signer(key, system_rng(), emsa, format, provider)
         {}
#endif

      ~PK_Signer();

      PK_Signer(const PK_Signer&) = delete;
      PK_Signer& operator=(const PK_Signer&) = delete;

      /**
      * Sign a message all in one go
      * @param in the message to sign as a byte array
      * @param length the length of the above byte array
      * @param rng the rng to use
      * @return signature
      */
      std::vector<uint8_t> sign_message(const uint8_t in[], size_t length,
                                     RandomNumberGenerator& rng)
         {
         this->update(in, length);
         return this->signature(rng);
         }

      /**
      * Sign a message.
      * @param in the message to sign
      * @param rng the rng to use
      * @return signature
      */
      template<typename Alloc>
         std::vector<uint8_t> sign_message(const std::vector<uint8_t, Alloc>& in,
                                           RandomNumberGenerator& rng)
         {
         return sign_message(in.data(), in.size(), rng);
         }

      /**
      * Add a message part (single byte).
      * @param in the byte to add
      */
      void update(uint8_t in) { update(&in, 1); }

      /**
      * Add a message part.
      * @param in the message part to add as a byte array
      * @param length the length of the above byte array
      */
      void update(const uint8_t in[], size_t length);

      /**
      * Add a message part.
      * @param in the message part to add
      */
      template<typename Alloc>
      void update(const std::vector<uint8_t, Alloc>& in)
         {
         update(in.data(), in.size());
         }

      /**
      * Add a message part.
      * @param in the message part to add
      */
      void update(const std::string& in)
         {
         update(cast_char_ptr_to_uint8(in.data()), in.size());
         }

      /**
      * Get the signature of the so far processed message (provided by the
      * calls to update()).
      * @param rng the rng to use
      * @return signature of the total message
      */
      std::vector<uint8_t> signature(RandomNumberGenerator& rng);


      /**
      * Set the output format of the signature.
      * @param format the signature format to use
      */
      void set_output_format(Signature_Format format) { m_sig_format = format; }

      /**
      * Return an upper bound on the length of the signatures this
      * PK_Signer will produce
      */
      size_t signature_length() const;

   private:
      std::unique_ptr<PK_Ops::Signature> m_op;
      Signature_Format m_sig_format;
      size_t m_parts, m_part_size;
   };

/**
* Public Key Verifier. Use the verify_message() functions for small
* messages. Use multiple calls update() to process large messages and
* verify the signature by finally calling check_signature().
*/
class BOTAN_PUBLIC_API(2,0) PK_Verifier final
   {
   public:
      /**
      * Construct a PK Verifier.
      * @param pub_key the public key to verify against
      * @param emsa the EMSA to use (eg "EMSA3(SHA-1)")
      * @param format the signature format to use
      * @param provider the provider to use
      */
      PK_Verifier(const Public_Key& pub_key,
                  const std::string& emsa,
                  Signature_Format format = IEEE_1363,
                  const std::string& provider = "");

      ~PK_Verifier();

      PK_Verifier& operator=(const PK_Verifier&) = delete;
      PK_Verifier(const PK_Verifier&) = delete;

      /**
      * Verify a signature.
      * @param msg the message that the signature belongs to, as a byte array
      * @param msg_length the length of the above byte array msg
      * @param sig the signature as a byte array
      * @param sig_length the length of the above byte array sig
      * @return true if the signature is valid
      */
      bool verify_message(const uint8_t msg[], size_t msg_length,
                          const uint8_t sig[], size_t sig_length);
      /**
      * Verify a signature.
      * @param msg the message that the signature belongs to
      * @param sig the signature
      * @return true if the signature is valid
      */
      template<typename Alloc, typename Alloc2>
      bool verify_message(const std::vector<uint8_t, Alloc>& msg,
                          const std::vector<uint8_t, Alloc2>& sig)
         {
         return verify_message(msg.data(), msg.size(),
                               sig.data(), sig.size());
         }

      /**
      * Add a message part (single byte) of the message corresponding to the
      * signature to be verified.
      * @param in the byte to add
      */
      void update(uint8_t in) { update(&in, 1); }

      /**
      * Add a message part of the message corresponding to the
      * signature to be verified.
      * @param msg_part the new message part as a byte array
      * @param length the length of the above byte array
      */
      void update(const uint8_t msg_part[], size_t length);

      /**
      * Add a message part of the message corresponding to the
      * signature to be verified.
      * @param in the new message part
      */
      template<typename Alloc>
         void update(const std::vector<uint8_t, Alloc>& in)
         {
         update(in.data(), in.size());
         }

      /**
      * Add a message part of the message corresponding to the
      * signature to be verified.
      */
      void update(const std::string& in)
         {
         update(cast_char_ptr_to_uint8(in.data()), in.size());
         }

      /**
      * Check the signature of the buffered message, i.e. the one build
      * by successive calls to update.
      * @param sig the signature to be verified as a byte array
      * @param length the length of the above byte array
      * @return true if the signature is valid, false otherwise
      */
      bool check_signature(const uint8_t sig[], size_t length);

      /**
      * Check the signature of the buffered message, i.e. the one build
      * by successive calls to update.
      * @param sig the signature to be verified
      * @return true if the signature is valid, false otherwise
      */
      template<typename Alloc>
      bool check_signature(const std::vector<uint8_t, Alloc>& sig)
         {
         return check_signature(sig.data(), sig.size());
         }

      /**
      * Set the format of the signatures fed to this verifier.
      * @param format the signature format to use
      */
      void set_input_format(Signature_Format format);

   private:
      std::unique_ptr<PK_Ops::Verification> m_op;
      Signature_Format m_sig_format;
      size_t m_parts, m_part_size;
   };

/**
* Object used for key agreement
*/
class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final
   {
   public:

      /**
      * Construct a PK Key Agreement.
      * @param key the key to use
      * @param rng the random generator to use
      * @param kdf name of the KDF to use (or 'Raw' for no KDF)
      * @param provider the algo provider to use (or empty for default)
      */
      PK_Key_Agreement(const Private_Key& key,
                       RandomNumberGenerator& rng,
                       const std::string& kdf,
                       const std::string& provider = "");

#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
      /**
      * Construct a PK Key Agreement.
      * @param key the key to use
      * @param kdf name of the KDF to use (or 'Raw' for no KDF)
      * @param provider the algo provider to use (or empty for default)
      */
      BOTAN_DEPRECATED("Use constructor taking a RNG object")
      PK_Key_Agreement(const Private_Key& key,
                       const std::string& kdf,
                       const std::string& provider = "") :
         PK_Key_Agreement(key, system_rng(), kdf, provider)
         {}
#endif

      ~PK_Key_Agreement();

      // For ECIES
      PK_Key_Agreement& operator=(PK_Key_Agreement&&);
      PK_Key_Agreement(PK_Key_Agreement&&);

      PK_Key_Agreement& operator=(const PK_Key_Agreement&) = delete;
      PK_Key_Agreement(const PK_Key_Agreement&) = delete;

      /**
      * Perform Key Agreement Operation
      * @param key_len the desired key output size
      * @param in the other parties key
      * @param in_len the length of in in bytes
      * @param params extra derivation params
      * @param params_len the length of params in bytes
      */
      SymmetricKey derive_key(size_t key_len,
                              const uint8_t in[],
                              size_t in_len,
                              const uint8_t params[],
                              size_t params_len) const;

      /**
      * Perform Key Agreement Operation
      * @param key_len the desired key output size
      * @param in the other parties key
      * @param params extra derivation params
      * @param params_len the length of params in bytes
      */
      SymmetricKey derive_key(size_t key_len,
                              const std::vector<uint8_t>& in,
                              const uint8_t params[],
                              size_t params_len) const
         {
         return derive_key(key_len, in.data(), in.size(),
                           params, params_len);
         }

      /**
      * Perform Key Agreement Operation
      * @param key_len the desired key output size
      * @param in the other parties key
      * @param in_len the length of in in bytes
      * @param params extra derivation params
      */
      SymmetricKey derive_key(size_t key_len,
                              const uint8_t in[], size_t in_len,
                              const std::string& params = "") const
         {
         return derive_key(key_len, in, in_len,
                           cast_char_ptr_to_uint8(params.data()),
                           params.length());
         }

      /**
      * Perform Key Agreement Operation
      * @param key_len the desired key output size
      * @param in the other parties key
      * @param params extra derivation params
      */
      SymmetricKey derive_key(size_t key_len,
                              const std::vector<uint8_t>& in,
                              const std::string& params = "") const
         {
         return derive_key(key_len, in.data(), in.size(),
                           cast_char_ptr_to_uint8(params.data()),
                           params.length());
         }

      /**
      * Return the underlying size of the value that is agreed.
      * If derive_key is called with a length of 0 with a "Raw"
      * KDF, it will return a value of this size.
      */
      size_t agreed_value_size() const;

   private:
      std::unique_ptr<PK_Ops::Key_Agreement> m_op;
   };

/**
* Encryption using a standard message recovery algorithm like RSA or
* ElGamal, paired with an encoding scheme like OAEP.
*/
class BOTAN_PUBLIC_API(2,0) PK_Encryptor_EME final : public PK_Encryptor
   {
   public:
      size_t maximum_input_size() const override;

      /**
      * Construct an instance.
      * @param key the key to use inside the encryptor
      * @param rng the RNG to use
      * @param padding the message encoding scheme to use (eg "OAEP(SHA-256)")
      * @param provider the provider to use
      */
      PK_Encryptor_EME(const Public_Key& key,
                       RandomNumberGenerator& rng,
                       const std::string& padding,
                       const std::string& provider = "");

#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
      /**
      * Construct an instance.
      * @param key the key to use inside the encryptor
      * @param padding the message encoding scheme to use (eg "OAEP(SHA-256)")
      */
      BOTAN_DEPRECATED("Use constructor taking a RNG object")
      PK_Encryptor_EME(const Public_Key& key,
                       const std::string& padding,
                       const std::string& provider = "") :
         PK_Encryptor_EME(key, system_rng(), padding, provider) {}
#endif

      ~PK_Encryptor_EME();

      PK_Encryptor_EME& operator=(const PK_Encryptor_EME&) = delete;
      PK_Encryptor_EME(const PK_Encryptor_EME&) = delete;

      /**
      * Return an upper bound on the ciphertext length for a particular
      * plaintext input length
      */
      size_t ciphertext_length(size_t ptext_len) const override;
   private:
      std::vector<uint8_t> enc(const uint8_t[], size_t,
                             RandomNumberGenerator& rng) const override;

      std::unique_ptr<PK_Ops::Encryption> m_op;
   };

/**
* Decryption with an MR algorithm and an EME.
*/
class BOTAN_PUBLIC_API(2,0) PK_Decryptor_EME final : public PK_Decryptor
   {
   public:
     /**
      * Construct an instance.
      * @param key the key to use inside the decryptor
      * @param rng the random generator to use
      * @param eme the EME to use
      * @param provider the provider to use
      */
      PK_Decryptor_EME(const Private_Key& key,
                       RandomNumberGenerator& rng,
                       const std::string& eme,
                       const std::string& provider = "");


#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
      /**
      * Construct an instance.
      * @param key the key to use inside the decryptor
      * @param eme the message encoding scheme to use (eg "OAEP(SHA-256)")
      */
      BOTAN_DEPRECATED("Use constructor taking a RNG object")
      PK_Decryptor_EME(const Private_Key& key,
                       const std::string& eme,
                       const std::string& provider = "") :
         PK_Decryptor_EME(key, system_rng(), eme, provider) {}
#endif

      size_t plaintext_length(size_t ptext_len) const override;

      ~PK_Decryptor_EME();
      PK_Decryptor_EME& operator=(const PK_Decryptor_EME&) = delete;
      PK_Decryptor_EME(const PK_Decryptor_EME&) = delete;
   private:
      secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
                                     const uint8_t in[],
                                     size_t in_len) const override;

      std::unique_ptr<PK_Ops::Decryption> m_op;
   };

/**
* Public Key Key Encapsulation Mechanism Encryption.
*/
class BOTAN_PUBLIC_API(2,0) PK_KEM_Encryptor final
   {
   public:
      /**
      * Construct an instance.
      * @param key the key to use inside the encryptor
      * @param rng the RNG to use
      * @param kem_param additional KEM parameters
      * @param provider the provider to use
      */
      PK_KEM_Encryptor(const Public_Key& key,
                       RandomNumberGenerator& rng,
                       const std::string& kem_param = "",
                       const std::string& provider = "");

#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
      BOTAN_DEPRECATED("Use constructor taking a RNG object")
      PK_KEM_Encryptor(const Public_Key& key,
                       const std::string& kem_param = "",
                       const std::string& provider = "") :
         PK_KEM_Encryptor(key, system_rng(), kem_param, provider) {}
#endif

      ~PK_KEM_Encryptor();

      PK_KEM_Encryptor& operator=(const PK_KEM_Encryptor&) = delete;
      PK_KEM_Encryptor(const PK_KEM_Encryptor&) = delete;

      /**
      * Generate a shared key for data encryption.
      * @param out_encapsulated_key the generated encapsulated key
      * @param out_shared_key the generated shared key
      * @param desired_shared_key_len desired size of the shared key in bytes
      * @param rng the RNG to use
      * @param salt a salt value used in the KDF
      * @param salt_len size of the salt value in bytes
      */
      void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
                   secure_vector<uint8_t>& out_shared_key,
                   size_t desired_shared_key_len,
                   Botan::RandomNumberGenerator& rng,
                   const uint8_t salt[],
                   size_t salt_len);

      /**
      * Generate a shared key for data encryption.
      * @param out_encapsulated_key the generated encapsulated key
      * @param out_shared_key the generated shared key
      * @param desired_shared_key_len desired size of the shared key in bytes
      * @param rng the RNG to use
      * @param salt a salt value used in the KDF
      */
      template<typename Alloc>
         void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
                      secure_vector<uint8_t>& out_shared_key,
                      size_t desired_shared_key_len,
                      Botan::RandomNumberGenerator& rng,
                      const std::vector<uint8_t, Alloc>& salt)
         {
         this->encrypt(out_encapsulated_key,
                       out_shared_key,
                       desired_shared_key_len,
                       rng,
                       salt.data(), salt.size());
         }


      /**
      * Generate a shared key for data encryption.
      * @param out_encapsulated_key the generated encapsulated key
      * @param out_shared_key the generated shared key
      * @param desired_shared_key_len desired size of the shared key in bytes
      * @param rng the RNG to use
      */
      void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
                   secure_vector<uint8_t>& out_shared_key,
                   size_t desired_shared_key_len,
                   Botan::RandomNumberGenerator& rng)
         {
         this->encrypt(out_encapsulated_key,
                       out_shared_key,
                       desired_shared_key_len,
                       rng,
                       nullptr,
                       0);
         }

   private:
      std::unique_ptr<PK_Ops::KEM_Encryption> m_op;
   };

/**
* Public Key Key Encapsulation Mechanism Decryption.
*/
class BOTAN_PUBLIC_API(2,0) PK_KEM_Decryptor final
   {
   public:
      /**
      * Construct an instance.
      * @param key the key to use inside the decryptor
      * @param rng the RNG to use
      * @param kem_param additional KEM parameters
      * @param provider the provider to use
      */
      PK_KEM_Decryptor(const Private_Key& key,
                       RandomNumberGenerator& rng,
                       const std::string& kem_param = "",
                       const std::string& provider = "");

#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
      BOTAN_DEPRECATED("Use constructor taking a RNG object")
      PK_KEM_Decryptor(const Private_Key& key,
                       const std::string& kem_param = "",
                       const std::string& provider = "") :
         PK_KEM_Decryptor(key, system_rng(), kem_param, provider)
         {}
#endif

      ~PK_KEM_Decryptor();
      PK_KEM_Decryptor& operator=(const PK_KEM_Decryptor&) = delete;
      PK_KEM_Decryptor(const PK_KEM_Decryptor&) = delete;

      /**
      * Decrypts the shared key for data encryption.
      * @param encap_key the encapsulated key
      * @param encap_key_len size of the encapsulated key in bytes
      * @param desired_shared_key_len desired size of the shared key in bytes
      * @param salt a salt value used in the KDF
      * @param salt_len size of the salt value in bytes
      * @return the shared data encryption key
      */
      secure_vector<uint8_t> decrypt(const uint8_t encap_key[],
                                  size_t encap_key_len,
                                  size_t desired_shared_key_len,
                                  const uint8_t salt[],
                                  size_t salt_len);

      /**
      * Decrypts the shared key for data encryption.
      * @param encap_key the encapsulated key
      * @param encap_key_len size of the encapsulated key in bytes
      * @param desired_shared_key_len desired size of the shared key in bytes
      * @return the shared data encryption key
      */
      secure_vector<uint8_t> decrypt(const uint8_t encap_key[],
                                  size_t encap_key_len,
                                  size_t desired_shared_key_len)
         {
         return this->decrypt(encap_key, encap_key_len,
                              desired_shared_key_len,
                              nullptr, 0);
         }

      /**
      * Decrypts the shared key for data encryption.
      * @param encap_key the encapsulated key
      * @param desired_shared_key_len desired size of the shared key in bytes
      * @param salt a salt value used in the KDF
      * @return the shared data encryption key
      */
      template<typename Alloc1, typename Alloc2>
         secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc1>& encap_key,
                                     size_t desired_shared_key_len,
                                     const std::vector<uint8_t, Alloc2>& salt)
         {
         return this->decrypt(encap_key.data(), encap_key.size(),
                              desired_shared_key_len,
                              salt.data(), salt.size());
         }

   private:
      std::unique_ptr<PK_Ops::KEM_Decryption> m_op;
   };

}

#endif
