///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCMS_CRYPTO_H
#define CPPCMS_CRYPTO_H

#include <cppcms/defs.h>
#include <booster/noncopyable.h>
#include <booster/hold_ptr.h>
#include <memory>
#include <string>

namespace cppcms {
	///
	/// \brief This namespace holds basic cryptographic utilities useful for save interaction with user.
	///
	/// One of the limitations of these functions is the fact that they do not use so called cryptographic memory,
	/// however it is not required as all secret keys are stored as plain text in configuration files,
	/// The only protection that is given is that values of keys are erased when the object is deleted
	/// to prevent key-leaks due to use of uninitialized memory in user applications.
	///
	///
	namespace crypto {
		///
		/// \brief Key object, holds the string that represents the binary key.
		///
		/// When the key is destroyed it zeros all the memory it uses to prevent accidental
		/// leaks of the highly confidential data
		///
		class CPPCMS_API key {
		public:
			///
			/// Create an empty key on 0 length
			///
			key();
			///
			/// Copy the key
			///
			key(key const &other);
			///
			/// Assign the key
			///
			key const &operator=(key const &);
			///
			/// Destroy they key object clearing the area used by it.
			///
			~key();
			///
			/// Create a key using binary representation pointed by \a data of \a length bytes
			///
			key(void const *data,size_t length);
			///
			/// Create a key using the hexadecimal representation
			///
			explicit key(char const *s);
			///
			/// Create a key using the hexadecimal representation
			///
			explicit key(std::string const &);
			///
			/// Get the pointer to data, never returns NULL even if size() == 0
			///
			char const *data() const;
			///
			/// Get the size of the key
			///
			size_t size() const;

			///
			/// Clear the key - and clear the area it was stored it
			///
			void reset();

			///
			/// Set the binary value for the key
			///
			void set(void const *ptr,size_t len);
			///
			/// Set the value for the key using hexadecimal representation 
			///
			void set_hex(char const *ptr,size_t len);
			
			///
			/// Read the key from file. Under windows file_name should be UTF-8 encoded
			/// string.
			///
			void read_from_file(std::string const &file_name);

		private:
			static unsigned from_hex(char c);
			char *data_;
			size_t size_;
		};
		///
		/// \brief this class provides an API to calculate various cryptographic hash functions
		///
		class CPPCMS_API message_digest : public booster::noncopyable {
		protected:
			/// It should be implemented in derived classes
			message_digest()
			{
			}
		public:
			virtual ~message_digest()
			{
			}
			
			///
			/// Get the size of message digest, for example for MD5 it is 16, for SHA1 it is 20
			///
			virtual unsigned digest_size() const = 0;
			///
			/// Get processing block size, returns 64 or 128, used mostly for correct HMAC calculations
			///
			virtual unsigned block_size() const = 0;

			///
			/// Add more data of size bytes for processing
			///
			virtual void append(void const *ptr,size_t size) = 0;
			///
			/// Read the message digest for the data and reset it into initial state,
			/// provided buffer must be digest_size() bytes
			///
			virtual void readout(void *ptr) = 0;

			///
			/// Make a polymorphic copy of this object, note the state of copied object is reset to 
			/// initial
			///
			virtual message_digest *clone() const = 0;

			///
			/// Get the name of the hash function
			///
			virtual char const *name() const = 0;

			///
			/// Create MD5 message digest
			///
			static std::auto_ptr<message_digest> md5();
			///
			/// Create SHA1 message digest
			///
			static std::auto_ptr<message_digest> sha1();
			///
			/// Create message digest by name, more then sha1 and md5 may be supported,
			/// if CppCMS is compiled with cryptography library like libgcrypt or openssl
			///
			static std::auto_ptr<message_digest> create_by_name(std::string const &name);
		};
		
		///
		/// \brief This object calculates the HMAC signature for the input data
		///
		class CPPCMS_API hmac : public booster::noncopyable  {
		public:
			///
			/// Create hmac that uses given \a digest algorithm and a binary key - \a key
			///
			hmac(std::auto_ptr<message_digest> digest,key const &k);
			///
			/// Create hmac that uses message digest algorithm called \a name and use a binary key - \a key
			///
			hmac(std::string const &name,key const &k);
			~hmac();

			///
			/// Get the size of the signtature
			///
			unsigned digest_size() const;

			///
			/// Add data for signing
			///
			void append(void const *ptr,size_t size);

			///
			/// Get the signature for all the data, after calling this function
			/// the state of the hmac is reset and it can be used for signing again
			///
			/// Note: provided buffer must be digest_size() bytes long.
			///
			void readout(void *ptr);
		private:
			void init();	
			struct data_;
			booster::hold_ptr<data_> d;
			std::auto_ptr<message_digest> md_,md_opad_;
			key key_;
		};

		///
		/// \brief  Cipher-block chaining encryption and decryption cryptographic service.
		/// 
		/// \note In order to use it, you \b must compile CppCMS with OpenSSL (libcrypto) or GNU-TLS (libgcrypt) library.
		///
		class CPPCMS_API cbc  : public booster::noncopyable {
		public:
			///
			/// CBC encryption type
			///
			typedef enum {
				aes128 	= 0,	///< AES-128
				aes192	= 1,	///< AES-192
				aes256	= 2 	///< AES-256
			} cbc_type;
		
			///
			/// Create a new cbc object that performs encryption using \a type method.
			///
			/// If the encryption method is not supported returns an empty pointer! 
			///
			static std::auto_ptr<cbc> create(cbc_type type);
			///
			/// Create a new cbc object that performs encryption using algorithm \a name
			///
			/// If the encryption method is not supported returns an empty pointer! 
			///
			/// Currently supported aes128, aes192, aes256, with names "aes" = "aes-128" = "aes128" , "aes-192" "aes192",
			/// "aes-256" = "aes256". They require CppCMS to be compiled with OpenSSL or GNU-TLS library
			///
			static std::auto_ptr<cbc> create(std::string const &name);

			///
			/// Get the size of the block CBC works on
			///
			virtual unsigned block_size() const = 0;
			///
			/// Get the required key size in bytes
			///
			virtual unsigned key_size() const = 0;

			///
			/// Set the key value
			///
			virtual void set_key(key const &) = 0;
			///
			/// Set initial vector value, size should be equal  to block_size()
			///
			virtual void set_iv(void const *ptr,size_t size) = 0;
			///
			/// Set randomly created initial vector value
			///
			virtual void set_nonce_iv() = 0;
			///
			/// Encrypt the data \a in to \a out of size \a len. \a len should be multiple of block_size()
			///
			virtual void encrypt(void const *in,void *out,unsigned len) = 0;
			///
			/// Decrypt the data \a in to \a out of size \a len. \a len should be multiple of block_size()
			///
			virtual void decrypt(void const *in,void *out,unsigned len) = 0;

			virtual ~cbc() 
			{
			}
			
		};

	} // crypto

} // cppcms



#endif
