// ---------------------------------------------------------------------------
// - Key.hpp                                                                 -
// - afnix:sec module - key class definition                                 -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  is  distributed in  the hope  that it will be useful, but -
// - without  any  warranty;  without  even   the   implied    warranty   of -
// - merchantability or fitness for a particular purpose.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2023 amaury darsch                                   -
// ---------------------------------------------------------------------------

#ifndef  AFNIX_KEY_HPP
#define  AFNIX_KEY_HPP

#ifndef  AFNIX_BUFFER_HPP
#include "Buffer.hpp"
#endif

#ifndef  AFNIX_RELATIF_HPP
#include "Relatif.hpp"
#endif

#ifndef  AFNIX_TAGGABLE_HPP
#include "Taggable.hpp"
#endif

namespace afnix {

  /// The Key class is an original class used to store a particular key
  /// or to generate one. A key is designed to operate with a variery of
  /// cipher that can be either symmetric or asymmetric. In the symmetric
  /// case, the key is generally an array of bytes. Asymmetric key are 
  /// generally stored in the form of number list that can be computed or
  /// loaded by value. By default, a random 128 bit symmetric key is created.
  /// @author amaury darsch

  class Key : public Taggable {
  public:
    /// the type of keys
    enum t_ckey : t_byte {
      CKEY_KNIL = 0x00U, // nil key
      CKEY_KSYM = 0x01U, // symmetric key
      CKEY_KRSA = 0x02U, // rsa key
      CKEY_KMAC = 0x03U, // mac key
      CKEY_KDSA = 0x04U, // dsa key
      CKEY_KDHE = 0x05U  // dh key
    };

    /// the class of keys
    enum t_kkey {
      KKEY_CBLK, // block cipher
      KKEY_CPUB, // public cipher
      KKEY_CMAC, // mac key
      KKEY_CSRL, // serial cipher
      KKEY_SGNR  // signature
    };
    
    /// the key index accessor
    enum t_ikey {
      KRSA_PMOD, // rsa modulus
      KRSA_PEXP, // rsa public exponent
      KRSA_SEXP, // rsa secret exponent
      KRSA_PPRM, // rsa prime p
      KRSA_QPRM, // rsa prime q
      KRSA_CRTP, // rsa crt p
      KRSA_CRTQ, // rsa crt q
      KRSA_CRTI, // rsa crt i
      KDSA_PPRM, // dsa prime p
      KDSA_QPRM, // dsa prime q
      KDSA_PGEN, // dsa generator
      KDSA_SKEY, // dsa secret key
      KDSA_PKEY, // dsa public key
      KDHE_PPRM, // dh prime p
      KDHE_GGEN, // dh group generator
      KDHE_GORD, // dh group order
      KDHE_SKEY, // dh secret key
      KDHE_PKEY  // dh public key
    };

  private:
    /// the cipher key type
    t_ckey  d_type;
    /// the key structure
    union {
      struct s_knil* p_knil;
      struct s_ksym* p_ksym;
      struct s_krsa* p_krsa;
      struct s_kmac* p_kmac;
      struct s_kdsa* p_kdsa;
      struct s_kdhe* p_kdhe;
    };

  public:
    /// create a nil key
    Key (void);

    /// create a key by type
    /// @param type the key type
    Key (const t_ckey type);

    /// create a symetric key by octet string
    /// @param ostr the octet string key
    Key (const String& ostr);

    /// create a symetric key by buffer
    /// @param kbuf the buffer key
    Key (const Buffer& kbuf);

    /// create a key by type and bit size 
    /// @param type the key type
    /// @param bits the key size
    Key (const t_ckey type, const long bits);

    /// create a key by type and buffer
    /// @param type the key type
    /// @param kbuf the key buffer
    Key (const t_ckey type, const Buffer& kbuf);

    /// create a key by type and octet string
    /// @param type the key type
    /// @param ostr the octet string key
    Key (const t_ckey type, const String& ostr);

    /// create a key by type and byte buffer
    /// @param type the key type
    /// @param size the buffer size
    /// @param kval the key value buffer
    Key (const t_ckey type, const long size, const t_byte* kval);

    /// create a key by type and object vector
    /// @param type the key type
    /// @param ovec the object vector
    Key (const t_ckey type, const Vector& ovec);

    /// copy construct this key
    /// @param that the key key to copy
    Key (const Key& that);

    /// destroy this key
    ~Key (void);

    /// assign a key to this one
    /// @param that the key to assign
    Key& operator = (const Key& that);

    /// @return the class name
    String repr (void) const override;

    /// @return a clone of this object
    Object* clone (void) const override;

    /// @return the serial did
    t_word getdid (void) const override;

    /// @return the serial sid
    t_word getsid (void) const override;

    /// serialize this key
    /// @param os the output stream
    void wrstream (OutputStream& os) const override;

    /// deserialize this key
    /// @param is the input stream
    void rdstream (InputStream& os) override;

    /// reset this key
    virtual void reset (void);
    
    /// @return the key type
    virtual t_ckey gettype (void) const;

    /// @return  the key size in bits
    virtual long getbits (void) const;

    /// @return  the key size in bytes
    virtual long getsize (void) const;

    /// @return a formatted key representation
    virtual String format (void) const;

    /// @return a formatted key representation by type
    virtual String format (const t_ikey type) const;

    /// @return a relatif key by type
    virtual Relatif getrkey (const t_ikey type) const;

    /// @return a key byte by index
    virtual t_byte getbyte (const long index) const;

    /// @return true if the key is valid
    virtual bool valid (void) const;
    
    /// @return true if the key is configured
    virtual bool isconfig (void) const;
    
    /// @return true if the key is public
    virtual bool ispublic (void) const;

    /// @return true if the key belongs to a class
    virtual bool isclass (const t_kkey kkey) const;
    
    /// @return a public key by removing the secret part
    virtual Key topublic (void) const;

    /// configure a key by type and size
    /// @param type the key type
    /// @param bits the key size
    virtual bool configure (const t_ckey type, const long bits);

    /// renewed this key
    virtual bool renew (void);
    
  public:
    /// evaluate an object data member
    /// @param zobj  the current evaluable
    /// @param nset  the current nameset
    /// @param quark the quark to evaluate
    static Object* meval (Evaluable* zobj, Nameset* nset, const long quark);

    /// create a new object in a generic way
    /// @param argv the argument vector
    static Object* mknew (Vector* argv);
    
    /// @return true if the given quark is defined
    bool isquark (const long quark, const bool hflg) const override;
 
    /// apply this object with a set of arguments and a quark
    /// @param zobj  the current evaluable
    /// @param nset  the current nameset
    /// @param quark the quark to apply these arguments
    /// @param argv  the arguments to apply
    Object* apply (Evaluable* zobj, Nameset* nset, const long quark,
                   Vector* argv) override;
  };
}

#endif
