/***************************************************************************
 $RCSfile: mediumkeyfile.h,v $
                             -------------------
    cvs         : $Id: mediumkeyfile.h,v 1.12 2003/03/02 19:35:15 derzach Exp $
    begin       : Thu Aug 22 2002
    copyright   : (C) 2002 by Martin Preuss
    email       : openhbci@aquamaniac.de

 ***************************************************************************
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library 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.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
 *   MA  02111-1307  USA                                                   *
 *                                                                         *
 ***************************************************************************/



#ifndef MEDIUMKEYFILE_H
#define MEDIUMKEYFILE_H


#include <openhbci/medium.h>
#include <openhbci/mediumrdhbase.h>
#include <openhbci/rsakey.h>
#include <openhbci/file.h>

namespace HBCI {


/** @short Implements a RDH medium with keys stored in a file.
 *
 * This abstract class implements the cryptographic functions of a RDH
 * medium where the keys are stored in a file. Note that this class is
 * still abstract since the actual storage of keys (and thus,
 * mountMedium and similar methods) is to be implemented in yet
 * another derived class. This class contains the key management
 * functions and the cryptographic functions for RDH medium.
 * */
class MediumKeyfileBase :public MediumRDHBase {
    friend class RDHConverter;

    //private:
    //string _path;
    //protected:
    //const string& path() const { return _path;};

 private:
    /**
     * @name User data and keys
     */
    /*@{*/
    Pointer<RSAKey> _userPubSignatureKey;
    Pointer<RSAKey> _userPrivateSignatureKey;
    Pointer<RSAKey> _userPubCryptKey;
    Pointer<RSAKey> _userPrivateCryptKey;

    Pointer<RSAKey> _tempPubSignatureKey;
    Pointer<RSAKey> _tempPrivateSignatureKey;
    Pointer<RSAKey> _tempPubCryptKey;
    Pointer<RSAKey> _tempPrivateCryptKey;
    string _userId;
    /*@}*/

    /**
     * @name Bank data and keys
     */
    /*@{*/
    int _country;
    string _instcode;
    string _systemid;
    Pointer<RSAKey> _instPubSignKey;
    Pointer<RSAKey> _instPubCryptKey;
    unsigned int _seq;
    const static string str_empty;
    /*@}*/

    void _clearMedium();
    Pointer<RSAKey> _readKey(const string &data);
    string _writeKey(Pointer<RSAKey> key) const;
 protected:
    /** Creates a context in your medium. A context is just the
     * combination of a userid and the institute code (+ country
     * code).
     *
     * Sets some context data in this medium.
     * @author Martin Preuss<openhbci@aquamaniac.de> */
    bool createMedium(int country,
                      const string &instcode,
                      const string &userid);

    Error readContext(const string &data);

    string writeContext() const;

 public:
    /**
     * Constructor.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @param hbci pointer to the HBCI object your program uses.
     * @param path full path to the file which serves as a medium. 
     * All keys are stored in this file.
     */
    //MediumKeyfileBase(const Hbci *hbci, const string &path);
    /**
     * Constructor.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @param hbci pointer to the HBCI object your program uses.
     */
    MediumKeyfileBase(const Hbci *hbci);

    ~MediumKeyfileBase();

    /** Since some media (like hbci chip cards) are able to store
     * multiple account entries you have to select one.
     *
     * A KeyfileMedium now only supports only one context per medium,
     * so this method only checks if the given data matches that of
     * the context stored in this class.
     *
     * @author Martin Preuss<openhbci@aquamaniac.de> */
    bool selectContext(int country,
                       const string &instcode,
                       const string &userid);

    /**
     * Returns the id of the medium. For DDV this is the CID of the chip
     * card, for RDH this is the system ID assigned to us by an institute.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    string mediumId() const { return _systemid;};

    /**
     * Returns the security mode of this medium (DDV, RDH)
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    int securityMode() const { return HBCI_SECURITY_RDH;};

	/**
	 * Change the PIN that is used to protect the medium data.<br>
	 * The old and new PIN is requested via the Hbci::interactor()
	 */
	virtual bool changePIN();

	/**
	 * Change data that is stored on the medium.<br>
	 * Note: Each paramter you don't specify is <b>not</b> changed.<br>
	 * @param context Key files currently support ony one context, use "1"
	 * @param country The new country code (280 for Germany)
	 * @param instcode The new institute code
	 * @param userid The new user id
	 * @param custid The new customer id (not supported with key files)
	 * @param server The new server address (not supported with key files)
	 */
	virtual bool changeContext(int context, int country=0, 
							   const string instcode="", 
							   const string userid="", const string custid="",
							   const string server="");

    unsigned int nextSEQ();

    /**
     * Sets the seq counter to 1
     */
    void resetSEQ();

    /**
     * Returns the number of the sign key. RDH-media should return
     * the number of the users private sign key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    int signKeyNumber() const;

    /**
     * Returns the version of the crypt key. RDH-media should return
     * the version of the users private sign key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    int signKeyVersion() const;

    /**
     * Returns the number of the crypt key. RDH-media should return
     * the number of the institutes public crypt key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    int cryptKeyNumber() const;

    /**
     * Returns the version of the crypt key. RDH-media should return
     * the version of the institutes public crypt key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    int cryptKeyVersion() const;

    /**
     * Returns the user ID of the owner of the crypt key. RDH-media
     * should return the owner of the institutes public crypt
     * key. This is retrieved from HIISA segments.
     *
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    const string &cryptKeyOwner() const;

    /**
     * Returns the context with the given number.
     * Some media are able of storing multiple contexts (like chip cards).
     * Please note that the medium has to be mounted prior to calling this
     * method.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return true if a context with that number exists, false on error
     * @param num number of the context (starting with 1)
     * @param countrycode reference to an int var to receive the country code
     * of the context (280 for Germany)
     * @param instcode reference to a string variable to receive the institute
     * code of that context (German "Bankleitzahl")
     * @param userid reference to a string variable to receive the user id of
     * the context (assigned to you by the institute)
     * @param server reference to a string to receive the server address
     */
    bool getContext(int num,
                    int &countrycode,
                    string &instcode,
                    string &userid,
                    string &server) const;
    /**
     * Creates a key for encryption of data.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return false on error
     */
    string createMessageKey() const;

    /**
     * Lets the card encrypt the key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return false on error
     * @param srckey the key to encode
     */
    string encryptKey(const string &srckey);

    /**
     * Lets the card decrypt the key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return false on error
     * @param srckey the key to decode
     */
    string decryptKey(const string &srckey);

    /**
     * Verify the signature of given data
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return false on error
     * @param data data whose signatur is to be verified
     * @param signature signature to compare against
     */
    bool verify(const string &data, const string &signature);

    /**
     * sign data
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return false on error
     * @param data data whose signatur is to be created
     */
    string sign(const string &data);

    /**
     * Generates the users sign and crypt keys.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @param activate if true then the key will be activated immediately.
     *  Otherwise just a temporary key will be created which must be activated
     *  by calling @ref activateKey(). If the key is not activated you can get
     * it by calling @ref getTempSignKey() and @ref getTempCryptKey().
     */
    virtual bool createUserKeys(bool activate=true) ;

    /**
     * Activates the previously created keys. This is <b>required</b> if
     * you specified "false" as arguement to @ref createUserKeys().
     * This will finally store the created keys to their destination, so that
     * they are usable for further signing/crypting.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    virtual bool activateKeys();

    /**
     * This returns the temporary sign key which has been created by
     * @ref createUserKeys(). You'll need this in a key change protocol.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    Pointer<RSAKey> getTempSignKey();

    /**
     * This returns the temporary crypt key which has been created by
     * @ref createUserKeys(). You'll need this in a key change protocol.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    Pointer<RSAKey> getTempCryptKey();

    /**
     * Sets the institutes public crypt key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    bool setInstituteCryptKey(Pointer<RSAKey> cryptkey);

    /**
     * Sets the institutes public sign key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    bool setInstituteSignKey(Pointer<RSAKey> signkey);

    /**
     * Sets the SystemId.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    void setSystemId(const string &newid);

    /** Returns true, if the institute/bank of this MediumRDH uses a
     * public signature key. */
    bool hasInstSignKey() const;

    /**
     * Returns the users public crypt key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    Pointer<RSAKey> userPubCryptKey() const;

    /**
     * Returns the users public sign key.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    Pointer<RSAKey> userPubSignKey() const;

    /** @name Ini-Letter Information */
    /*@{*/
    /** Returns the modulus of the institute key that is needed to
     * print the ini-letter.
     *
     * @param crypt If true, then the data of <i>crypt</i> key is read
     * instead of that of the <i>sign</i> key. This exists for the
     * case when your institute does not sign its messages. In that
     * not-so-good case, MediumRDH::hasInstSignKey returned false (the
     * inverse of this argument). If unsure, leave it to the default
     * value <code>false</code>.  */
    string getInstIniLetterModulus(bool crypt=false) const;

    /**
     * return the ini-letter-exponent that is needed to print the ini-letter
     *
     * @param crypt If true, then the data of <i>crypt</i> key is read
     * instead of that of the <i>sign</i> key. This exists for the
     * case when your institute does not sign its messages. In that
     * not-so-good case, MediumRDH::hasInstSignKey returned false (the
     * inverse of this argument). If unsure, leave it to the default
     * value <code>false</code>.  */
    string getInstIniLetterExponent(bool crypt=false) const;

    /**
     * return the ini-letter-hash that is needed to print the ini-letter
     *
     * @param crypt If true, then the data of <i>crypt</i> key is read
     * instead of that of the <i>sign</i> key. This exists for the
     * case when your institute does not sign its messages. In that
     * not-so-good case, MediumRDH::hasInstSignKey returned false (the
     * inverse of this argument). If unsure, leave it to the default
     * value <code>false</code>. 
     */
    string getInstIniLetterHash(bool crypt=false) const;

    /**
     * Returns the number of the institute key.
     *
     * @param usecrypt If true, then the data of <i>crypt</i> key is read
     * instead of that of the <i>sign</i> key. This exists for the
     * case when your institute does not sign its messages. In that
     * not-so-good case, MediumRDH::hasInstSignKey returned false (the
     * inverse of this argument). If unsure, leave it to the default
     * value <code>false</code>. 
     */
    int getInstKeyNumber(bool usecrypt = false) const;
    /**
     * Returns the version of the institute key.
     *
     * @param usecrypt If true, then the data of <i>crypt</i> key is read
     * instead of that of the <i>sign</i> key. This exists for the
     * case when your institute does not sign its messages. In that
     * not-so-good case, MediumRDH::hasInstSignKey returned false (the
     * inverse of this argument). If unsure, leave it to the default
     * value <code>false</code>. 
     */
    int getInstKeyVersion(bool usecrypt = false) const;

    /**
     * return the ini-letter-modulus that is needed to print the ini-letter
     */
    string getUserIniLetterModulus() const;

    /**
     * return the ini-letter-exponent that is needed to print the ini-letter
     */
    string getUserIniLetterExponent() const;

    /**
     * return the ini-letter-hash that is needed to print the ini-letter
     */
    string getUserIniLetterHash() const;

    /**
     * This method is needed when generating the users ini letter.
     * @return the number of the user's public crypt key
     */
    int getUserKeyNumber() const;

    /**
     * This method is needed when generating the users ini letter.
     * @return the version of the user's public crypt key
     */
    int getUserKeyVersion() const;
    /*@}*/


};





/** @short Implements the storage part of a RDH medium with keys
 * stored in a file.
 *
 * This class implements storage functions of the file-based RDH
 * medium. Opposed to that, key management and cryptographic functions
 * are already implemented in the base class.  This class only cares
 * about read/write functions (mountMedium etc.). I.e., the actual
 * file format is implemented in this class. */
class MediumKeyfile :public MediumKeyfileBase {
private:
    int _mountCount;
    string _path;
    string _pin;
    Pointer<User> _lastMounter;

    Error _reallyReadFile(File f, string &result);
    Error _readFile(const string &path,
                    const string &pin);
    Error _writeFile(const string &path,
                     const string &pin);
public:
    /**
     * Constructor.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @param hbci pointer to the HBCI object your program uses.
     * @param path full path to the file which serves as a medium. 
     * All keys are stored in this file.
     */
    MediumKeyfile(const Hbci *hbci, const string &path);

    ~MediumKeyfile();

    /** Creates a context in your medium. A context is just the
     * combination of a userid and the institute code (+ country
     * code).
     *
     * Sets some context data in this medium.
     * @author Martin Preuss<openhbci@aquamaniac.de> */
    bool createMedium(int country,
                      const string &instcode,
                      const string &userid,
                      const string &pin="");

    /**
     * Mounts a medium and makes its crypto methods available.
     *
     * @param user Pointer to the user that wants to mount this
     * medium. Used only for information purposes in the
     * HBCI::Interactor. May be an invalid (NULL) pointer .
     *
     * @param pin The secret PIN that is needed to actually mount this
     * medium. If omitted the user will be asked for it when needed.
     *
     * @author Martin Preuss<openhbci@aquamaniac.de>
     *
     * @return An HBCI::Error, where Error.isOk()==true if medium mounted
     */
    Error mountMedium(Pointer<User> user = 0, const string &pin="");

    /**
     * Unmounts a medium so that it may be removed.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return true if medium unmounted
     */
    void unmountMedium(const string &pin="");

    /**
     * Checks whether the medium is mounted.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @return true if medium unmounted, false if not (or on error)
     */
    bool isMounted() { return _mountCount;};

    unsigned int nextSEQ();

    /**
     * resets the sequence counter, so that nextSEQ() will return 1
     */
    void resetSEQ();

    /**
     * Returns the id of the medium.
     * For DDV this is the card number, for RDH this is the name of the file.
     */
    const string& mediumName() const { return _path; };

    /**
     * Returns the type this medium is of.
     *
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    MediumType mediumType() const { return MediumRDHFile;};

    /**
     * Generates the users sign and crypt keys.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     * @param activate if true then the key will be activated immediately.
     *  Otherwise just a temporary key will be created which must be activated
     *  by calling @ref activateKey(). If the key is not activated you can get
     * it by calling @ref getTempSignKey() and @ref getTempCryptKey().
     */
    virtual bool createUserKeys(bool overwrite=false, bool activate=true) ;

    /**
     * Activates the previously created keys. This is <b>required</b> if
     * you specified "false" as arguement to @ref createUserKeys().
     * This will finally store the created keys to their destination, so that
     * they are usable for further signing/crypting.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    virtual bool activateKeys();

    /**
     * This returns the temporary sign key which has been created by
     * @ref createUserKeys(). You'll need this in a key change protocol.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    Pointer<RSAKey> getTempSignKey();

    /**
     * This returns the temporary crypt key which has been created by
     * @ref createUserKeys(). You'll need this in a key change protocol.
     * @author Martin Preuss<openhbci@aquamaniac.de>
     */
    Pointer<RSAKey> getTempCryptKey();

};


} // namespace


#endif
