#define NO_INCLUDE_LOCL

/*
 *
 */
 
#ifndef __CRD_UT10XX_H
#define __CRD_UT10XX_H

#include <usb.h>
#include <stdio.h>

#ifdef __cplusplus
extern "C"
{
#endif

#define CRD_CC_GCC 0
#define CRD_CC_MSV 1

/* GNU C compliller definitions. */
#if defined (__GNUC__)
#define CRD_CC CRD_CC_GCC
#define CRD_EXPORT_SYMBOL
#define CRD_IMPORT_SYMBOL
#define CRD_DECLARE extern
/* Microsoft C compiller definitions. */
#elif defined (_WIN32)
#define CRD_CC CRD_CC_MSV
#define CRD_EXPORT_SYMBOL __declspec( dllexport )
#define CRD_IMPORT_SYMBOL __declspec( dllimport )
#ifdef CRD_UT10XX_LIB_COMPILATION
#define CRD_DECLARE CRD_EXPORT_SYMBOL extern
#else
#define CRD_DECLARE CRD_IMPORT_SYMBOL extern
#endif
/* Other C compillers are not supported. */
#else
#error "Unknown compiller"
#endif

/**
 * \defgroup interface_group External CRD-UT10XX interfaces
 * \brief Module contains all interfaces avaliable for external applications.
 * \{
 */

/**
 * \defgroup native_interface_group Native interface
 * \brief Module defines basic interface to all device features.
 * \{
 */

/**
 * \name PIN identifiers
 * \{
 */

/** \brief Security Officer (SO) PIN identifier. */
#define CRD_UT10XX_SO_PIN_ID 0
/** \brief User PIN identified. */
#define CRD_UT10XX_USER_PIN_ID 1
/** \} */


/**
 * \name Maximum values
 * \{
 */

/** \brief Maximum size of host frame including frame length and type. */
#define HF_MAX 130
/** \brief Maximum size of device frame including frame length and type. */
#define DF_MAX 130
/** \brief Maximum number of files stored in device. */
#define FILES_NO_MAX 16
/** \} */

/**
 * \name Error codes
 * \{
 */

/** \brief Undefined error. */
#define ERR_CRD_UT10XX_UNDEF -1
/** \brief Session with device can not be open. */
#define ERR_CRD_UT10XX_LOCK -2
/** \brief Session with device can not be close. */
#define ERR_CRD_UT10XX_UNLOCK -3
/** \brief Session with device is closed. */
#define ERR_CRD_UT10XX_SESSION_CLOSED -4
/** \brief Error during message receive from device. */
#define ERR_CRD_UT10XX_RECEIVE -5
/** \brief Error during message transmition to device. */
#define ERR_CRD_UT10XX_TRANSMIT -6
/** \brief Buffer is too small to write all data. */
#define ERR_CRD_UT10XX_TOO_SMALL_BUF -7
/** \brief Buffer is to large to read it all. */
#define ERR_CRD_UT10XX_TOO_LARGE_BUF -8
/** \brief Library is unable to set up device interface. */
#define ERR_CRD_UT10XX_SET_UP -9
/** \brief This key number is not supported by device. */
#define ERR_CRD_UT10XX_WRONG_KEY -10
/** \brief Device is not supported by this version of library. */
#define ERR_CRD_UT10XX_NOT_SUPPORTED -11
/** \brief Argument is out of range. */
#define ERR_CRD_UT10XX_ARG_RANGE -12
/** \brief Parameter is not initialized. */
#define ERR_CRD_UT10XX_NOT_INIT -13

/** \brief General error transmited by device. */
#define ERR_CRD_UT10XX_DEV -100
/** \brief Device returns that command is wrong. */
#define ERR_CRD_UT10XX_DEV_WRONG_CMD (ERR_CRD_UT10XX_DEV - 0)
/** \brief Device returns that command is not allowed. */
#define ERR_CRD_UT10XX_DEV_CMD_NOT_ALLOWED (ERR_CRD_UT10XX_DEV - 1)
/** \brief Device returns that given PIN is wrong. */
#define ERR_CRD_UT10XX_DEV_WRONG_PIN (ERR_CRD_UT10XX_DEV - 2)
/** \brief Point passed to ECDH shared info primitive is not on curve. */
#define ERR_CRD_UT10XX_DEV_WRONG_EC_POINT (ERR_CRD_UT10XX_DEV - 3)
/** \brief Device returns that key of given number does not exist. */
#define ERR_CRD_UT10XX_DEV_NO_KEY (ERR_CRD_UT10XX_DEV - 4)
/** \brief Device returns that given arguments are out of range. */
#define ERR_CRD_UT10XX_DEV_ARG_RANGE (ERR_CRD_UT10XX_DEV - 5)
/** \brief Device returns that transmit frame has wrong syntax. */
#define ERR_CRD_UT10XX_DEV_FRAME_SYNTAX (ERR_CRD_UT10XX_DEV - 6)
/** \brief Device returns that transmit frame is out of range. */
#define ERR_CRD_UT10XX_DEV_FRAME_RANGE (ERR_CRD_UT10XX_DEV - 7)
/** \} */

/**
 * \brief Elliptic curve domain paramethers structure.
 */
typedef struct CRD_UT10XX_ecc_params_t
{
	/** \brief Unique id number. */
	int id;
	/** \brief Number of encoding octets for single field element. */
	int field_octets;
	/** \brief Number of encoding octets for generator order. */
	int order_octets;
	/** \brief ASCII string containing short name of domain. */
	const char *name;
	/** \brief Number of octets of asn1_named. */
	int asn1_named_octets;
	/** \brief ASN.1 object identifier of this domain. */
	const unsigned char *asn1_named;
	/** \brief ASN.1 encoding for AlgorithmIdentifier in SubjectPublicKeyInfo. */
	const unsigned char *asn1_named_alg;
	/** \brief Number of octets of asn1_named. */
	int asn1_specified_octets;
	/** \brief ASN.1 encoded domain paramether structure. */
	const unsigned char *asn1_specified;
}
CRD_UT10XX_ecc_params_t;

/**
 * \brief Function returns domain paramethers by its id. 
 *
 * \param[in] id -
 *   unique identifier of elliptic curve domain paramethers.
 *
 * \return
 *   - pointer to domain parameters structure with specified id,
 *   - 0 if there is no domain with this id.
 */
CRD_DECLARE const CRD_UT10XX_ecc_params_t* CRD_UT10XX_get_ecc_params(int id);

/**
 * \brief Configuration structure.
 */
typedef struct CRD_UT10XX_conf_t
{
	/** \brief Device information bits. */
	int info;
	/** \brief Device name octets. */
	char name[11];
	/** \brief Number of wrong SO PINs which cause device eraseing. */
	int so_pin_limit;
	/** \brief Number of wrong User PINs which cause device locking. */
	int user_pin_limit;
	/** \brief SO PIN. */
	char so_pin[8];
	/** \brief User PIN. */
	char user_pin[8];
}
CRD_UT10XX_conf_t;

/**
 * \brief Information structure. 
 */
typedef struct CRD_UT10XX_info_t
{
	/** \brief Device information bits. */
	int info;
	/** \brief Device name octets. */
	char name[11];
	/** \brief Actual number of wrong SO PIN trys. */
	int so_pin_trys;
	/** \brief Actual number of wrong User PIN trys. */
	int user_pin_trys;
	/** \brief Number of wrong SO PINs which cause device eraseing. */
	int so_pin_limit;
	/** \brief Number of wrong User PINs which cause device locking. */
	int user_pin_limit;
	/** \brief Main number of device firmware version. */
	int main_ver;
	/** \brief Sub number of device firmware version. */
	int sub_ver;
	/** \brief Number of total public memory octets. */
	int public_mem;
	/** \brief Number of avaliable public objects. */
	int public_objs;
	/** \brief Maximum number of ECC keys which can be stored. */
	int keys_num;
	/** \brief Number of supported ECC domains. */
	int ecc_num;
	/** \brief Pointer to supported domain. */
	const CRD_UT10XX_ecc_params_t *ecc_params;
}
CRD_UT10XX_info_t;

/**
 * \brief Key information structure.
 */
typedef struct CRD_UT10XX_key_info_t
{
	/** \brief Key information bits. */
	int info;
	/** \brief Pointer to ECC domain parameters for this key. */
	const CRD_UT10XX_ecc_params_t *ecc_params;
	/** \brief UTF-8 string which contains key name. */
	char name[22];
	/** \brief Encoding of public key X coordinate. */
	char pub_x[32];
	/** \brief Encoding of public key Y coordinate. */
	char pub_y[32];
}
CRD_UT10XX_key_info_t;

/**
 * \brief Device structure.
 */
typedef struct CRD_UT10XX_t
{
	/**
	 * \name LibUSB fetures
	 * \{
	 */
	
	/** \brief Pointer to USB device defined by LibUSB. */
	struct usb_device *hw;
	/** \brief Pointer to USB device handle defined by LibUSB. */
	struct usb_dev_handle *handle;
	/** \brief USB device product string. */
	char product_string[256];
	/** \brief USB device serial number. */
	char serial_number[256];
	/** \} */
	
	/**
	 * \name Low level interface functions
	 * \{
	 */
	
	/** \brief Function which opens device to read or write. */
	int (*open)(struct CRD_UT10XX_t *ut);
	/** \brief Function which closes device if it is no longer need. */
	int (*close)(struct CRD_UT10XX_t *ut);
	/** \brief Function which reads at most \a octets from device. */
	int (*read)(struct CRD_UT10XX_t *ut, unsigned char *buf, int octets, int timeout_ms);
	/** \brief Function which writes to device. */
	int (*write)(struct CRD_UT10XX_t *ut, const unsigned char *buf, int octets);
	/** \} */
	
	/**
	 * \name Device information and configuration structures
	 * \{
	 */
	
	/** \brief Device configuration structure. */
	CRD_UT10XX_conf_t conf;
	/** \brief Device information structure. */
	CRD_UT10XX_info_t info;
	/** \brief Information about device keys. */
	CRD_UT10XX_key_info_t keys[4];
	/** \} */
	
	/**
	 * \name Device file system information
	 * \{
	 */
	
	/** \brief Maximum number of files which can be stored. */
	int files_no;
	/** \brief Table with free sectors avaliable in device public memory. */
	unsigned char free_sectors[128];
	/** \brief Table with file types. */
	int file_types[FILES_NO_MAX];
	/** \brief Table with file sizes. */
	int file_sizes[FILES_NO_MAX];
	/** \brief Table with names of all files. */
	char file_names_tb[FILES_NO_MAX * 32];
	/** \brief Table of pointers to file names. */
	char *file_names[FILES_NO_MAX];
	/** \} */
	
	/**
	 * \name Implementation specific data.
	 * \{
	 */
	
	/** \brief Indicate if session with device is open or close. */
	int session_is_open;
	/** \brief Indicate if device is still attached to host. */
	int present;
	/** \brief Table for error stack. */
	char err[4096];
	/** \brief Pointer to next device attached to host. */
	struct CRD_UT10XX_t *next;
	/** \} */
}
CRD_UT10XX_t;

/**
 * \name Library management functions
 * \{
 */

/**
 * \brief Library initialization.
 *
 * \return
 *   - nonegative value if library is initialized properly,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_lib_init(void);

/**
 * \brief Library finish.
 *
 * \return
 *   - nonegative value if library is finished properly,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_lib_finish(void);

/**
 * \brief Find all devices attached to host.
 *
 * \return
 *   - nonegative value which is equal to number of founded devices,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_lib_find_devices(void);

/**
 * \brief Actualize library data structures.
 *
 * \return
 *   - zero if actualization was finished successfully,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_lib_actualize_devices(void);

/**
 * \brief Short description of library error.
 *
 * \param[in] err -
 *   library error code.
 *
 * \return
 *   - pointer to character string which describes error,
 *   - zero pointer if there is no such error code.
 */
CRD_DECLARE const char* CRD_UT10XX_lib_error_desc(int err);

/**
 * \brief Detail description of last ocured error.
 *
 * \param[in] ut -
 *   device for which error occured.
 *
 * \return Pointer to character string which describes error.
 */
CRD_DECLARE const char* CRD_UT10XX_lib_detail_error_desc(const CRD_UT10XX_t *ut);

/**
 * \brief Print detail description of last occured error to file.
 *
 * \param[in] ut -
 *   device for which error occured.
 * \param[in] fmt -
 *   format for single error line. Every line is printed to file \a fd by 
 *   command \c fprintf(fd, fmt, error_line). 
 * \param[in,out] fd -
 *   file open to write in text mode.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_lib_detail_error_desc_to_file(const CRD_UT10XX_t *ut, 
	const char *fmt, FILE *fd);
/** \} */

/**
 * \name Device access functions
 * \{
 */

/**
 * \brief Open session with device.
 *
 * This function has to be call before any other command. It reserves device 
 * for future use and does not allow other threads to use it in the same time.
 *
 * \param[in,out] ut -
 *   device to open.
 *
 * \return
 *   - zero value if device was opened properly,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_session_open(CRD_UT10XX_t *ut);

/**
 * \brief Close session with device.
 *
 * This function has to be call if device is no longer need.
 *
 * \param[in,out] ut -
 *   device to close.
 *
 * \return
 *   - zero value if device was closed properly,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_session_close(CRD_UT10XX_t *ut);

/**
 * \brief Get first device from library list.
 *
 * \return Pointer to first device (may be zero if no device is attached).
 */
CRD_DECLARE CRD_UT10XX_t* CRD_UT10XX_get_first(void);

/**
 * \brief Get device with given index number.
 *
 * \param[in] idx -
 *   number of device You want to get (devices are numbered from 0).
 *
 * \return Pointer to device with given index (may be zero if there is no 
 *   device with such number).
 */
CRD_DECLARE CRD_UT10XX_t* CRD_UT10XX_find_by_index(int idx);

/**
 * \brief Get device which has given name.
 *
 * \param[in] name -
 *   device name as UTF-8 string.
 *
 * \return Pointer to device with given name (may be zero if there is no 
 *   such device).
 */
CRD_DECLARE CRD_UT10XX_t* CRD_UT10XX_find_by_name(const char *name);

/**
 * \brief Get device which has given serial number.
 *
 * \param[in] serial_number -
 *   device USB serial number as UTF-8 string.
 *
 * \return Pointer to device with given serial number (may be zero if there is no 
 *   such device).
 */
CRD_DECLARE CRD_UT10XX_t* CRD_UT10XX_find_by_serial_number(const char *serial_number);

/**
 * \brief Find index of given device.
 *
 * \param[in] ut -
 *   pointer to device.
 *
 * \return
 *   - nonegative value which is equal to device index,
 *   - negative value when device was not founded.
 */
CRD_DECLARE int CRD_UT10XX_find_index_of(CRD_UT10XX_t *ut);

/**
 * \brief Find index of first ungenerated key.
 *
 * \param[in,out] ut - 
 *   device where key is searched.
 *
 * \return
 *   - nonegative value which is equal to key index,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_find_free_key(CRD_UT10XX_t *ut);

/**
 * \brief Find key with given name.
 *
 * \param[in,out] ut -
 *   device where key is searched.
 * \param[in] name -
 *   name of serched key.
 *
 * \return
 *   - nonegative value which is equal to key index,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_find_key_by_name(CRD_UT10XX_t *ut, const char *name);

/**
 * \brief Convert key to ASN.1 SubjectPublicKeyInfo.
 *
 * \param[in] key -
 *   key which should be convert to ASN.1 form.
 * \param[out] key_info -
 *   buffer for SubjectPublicKeyInfo ASN.1 encoding. If this pointer is 0, then 
 *   function returns number of encoding octets.
 * \param[in] key_info_octets -
 *   \a key_info buffer size.
 *
 * \return
 *   - nonegative value which is equal to SubjectPublicKeyInfo encoding,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_subject_public_key_info(const CRD_UT10XX_key_info_t *key, char *key_info, 
	int key_info_octets);
/** \} */

/**
 * \name Device command interface
 * \{
 */

/**
 * \brief Device initialization.
 *
 * Function is necessary to initialize internal data structures of the device. 
 * This call may take up to 40 seconds (it is dependent on supported domain).
 *
 * \param[in,out] ut -
 *   device to initalization (it has to be in CLEAR state).
 *
 * \return
 *   - nonegative value if device is initialized properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_dev_init(CRD_UT10XX_t *ut);

/**
 * \brief Device configuration.
 *
 * Function takes device \a ut and its member structure \a conf 
 * (\ref CRD_UT10XX_conf_t) to generate configuration command frame. 
 * During this operation such paramethers as:
 *   - device name,
 *   - configuration bits,
 *   - SO and User PIN,
 *   - limits or wrong PINs
 * are set up. Afer successfull call device is ready to its main tasks.
 *
 * \param[in,out] ut -
 *   device to configuration (it has to be in INIT state).
 *
 * \return
 *   - nonegative value if device is configured properly,
 *   - negative error code in other case.
 *
 * \attention 
 *   - Before configuration structure \a conf of \a ut device has 
 *     to be filled.
 *   - It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_dev_conf(CRD_UT10XX_t *ut);

/**
 * \brief Read information about device.
 *
 * After this call all avaliable informations about device are in \a info 
 * (\ref CRD_UT10XX_info_t) member structure of \a ut device.
 *
 * \param[in,out] ut -
 *   device about which information is readed.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 */
CRD_DECLARE int CRD_UT10XX_cmd_read_dev_info(CRD_UT10XX_t *ut);

/**
 * \brief Read information about device key.
 *
 * After call of this function all avaliable informations about device key 
 * with given index \a key_num are in \a keys[\a key_num] 
 * (\ref CRD_UT10XX_key_info_t) member structure of \a ut device.
 *
 * \param[in,out] ut -
 *   device where information about key is searched (it has to be in CONF 
 *   state).
 * \param[in] key_num -
 *   key index.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_read_key_info(CRD_UT10XX_t *ut, int key_num);

/**
 * \brief Read object from device public memory.
 *
 * \param[in,out] ut -
 *   device from which public object is readed (it has to be in CONF state).
 * \param[in] obj_num -
 *   number of object to read. Public objects are numbered from 0 and total 
 *   of avaliable objects can be found in \a info (\ref CRD_UT10XX_info_t) 
 *   member structure of \a ut device.
 * \param[out] obj -
 *   buffer for readed data (it has to have at least \ref PUBLIC_OBJ_OCTETS 
 *   octets).
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_read_public_obj(CRD_UT10XX_t *ut, int obj_num, char *obj);

/**
 * \brief Generate random bits.
 *
 * Function generates truly random bits (about 1500 bits per second).
 *
 * \param[in,out] ut -
 *   device uses to random bits generation.
 * \param[out] buf -
 *   buffer for random bits.
 * \param[in] octets -
 *   number of random octets to generate.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_rbg_generate_bits(CRD_UT10XX_t *ut, char *buf, int octets);

/**
 * \brief Unlock device.
 *
 * \param[in,out] ut -
 *   device which you want to unlock (it has to be in LOCK state).
 * \param[in] pin -
 *   character string which contains SO PIN (no more than 8 characters).
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_dev_unlock(CRD_UT10XX_t *ut, const char *pin);

/**
 * \brief Erase device.
 *
 * Function clears all private memory objects such as PINs and cryptographic 
 * keys. After successfull call device is in CLEAR state.
 *
 * \param[in,out] ut -
 *   device you want to erase (it has to be in CONF state).
 * \param[in] pin -
 *   character string which contains SO PIN (no more than 8 characters).
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_dev_erase(CRD_UT10XX_t *ut, const char *pin);

/**
 * \brief Change SO PIN.
 *
 * \param[in,out] ut -
 *   device for which SO PIN you want to change (it has to be in CONF state).
 * \param[in] pin -
 *   character string which contains SO PIN (no more than 8 characters).
 * \param[in] new_pin -
 *   character string which contains new SO PIN.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_change_so_pin(CRD_UT10XX_t *ut, const char *pin, 
	const char *new_pin);

/**
 * \brief Generate signature using ECDSA.
 *
 * Function generates signature using given key and digest. Resuly is 
 * writen to \a sign buffer in ASN.1 DER format.
 * \code
 *   SIGNATURE ::= SEQUENCE 
 *   {
 *       INTEGER r,
 *       INTEGER s
 *   }
 * \endcode
 *
 * \param[in,out] ut -
 *   device you want to use to signature generation (it has to be in CONF 
 *   state and has at least one key).
 * \param[in] pin -
 *   character string which contains User PIN (no more than 8 characters).
 * \param[in] key_num -
 *   index of key you want to use to sign.
 * \param[out] sign -
 *   buffer for signature.
 * \param[in] sign_octets -
 *   size of \a sign buffer in octets.
 * \param[in] digest -
 *   message digest to sigm.
 * \param[in] digest_octets -
 *   size of \a digest in octets.
 *
 * \return
 *   - nonegative value which is equal to signature len if operation is 
 *     done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_ecdsa_sign(CRD_UT10XX_t *ut, const char *pin, int key_num, 
	char *sign, int sign_octets, const char *digest, int digest_octets);

/**
 * \brief Change User PIN.
 *
 * \param[in,out] ut -
 *   device for which User PIN is changed (it has to be in CONF state).
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 * \param[in] new_pin -
 *   character string which contains new User PIN.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention 
 *   - Changing User PIN by SO is allowed only if UPCNA (User Pin Change Not 
 *     Allowed) configuration bit is set.
 *   - It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_change_user_pin(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	const char *new_pin);

/**
 * \brief Generate cryptographic key.
 *
 * \param[in,out] ut -
 *   device for which ECC  key is generated (it has to be in CONF state).
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 * \param[in] key_name -
 *   character string which contains name of generated key.
 * \param[in] key_num -
 *   index of generated key.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_cmd_generate_key(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	const char *key_name, int key_num);

/**
 * \brief Delete cryptographic key.
 *
 * \param[in,out] ut -
 *   device for which key is removed (it has to be in CONF state).
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 * \param[in] key_num -
 *   index of key to delete.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_delete_key(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	int key_num);

/**
 * \brief Write object to device public memory.
 *
 * \param[in,out] ut -
 *   device for which public object is written (it has to be in CONF state).
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 * \param[in] obj_num -
 *   number of object to write. Public objects are numbered from 0 and total 
 *   of avaliable objects can be found in \a info (\ref CRD_UT10XX_info_t) 
 *   member structure of \a ut device.
 * \param[in] obj -
 *   buffer for readed data (it has to have at least \ref PUBLIC_OBJ_OCTETS 
 *   octets).
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_cmd_write_public_obj(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	int obj_num, const char *obj);

/**
 * \brief Check if given PIN is correct.
 *
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 *
 * \return
 *   - nonegative value if operation is done properly and PIN is correct,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_cmd_try_pin(CRD_UT10XX_t *ut, const char *pin, int pin_id);

/** \} */

/** \} */


/**
 * \defgroup fs_interface_group File system interface
 * \brief Module gives basic interface to file system in public memeory. 
 *
 * \{
 */

/**
 * \name File types
 * \{
 */

/** \brief Empty file. */
#define CRD_UT10XX_FT_EMPTY 0x00
/** \brief Uesr certificvate. */
#define CRD_UT10XX_FT_CRT 0x10
/** \brief CA certificvate. */
#define CRD_UT10XX_FT_CACRT 0x20
/** \brief Certification request file. */
#define CRD_UT10XX_FT_REQ 0x30
/** \brief Text file. */
#define CRD_UT10XX_FT_TXT 0x40
/** \brief HTML file. */
#define CRD_UT10XX_FT_HTML 0x50
/** \brief Configuration file. */
#define CRD_UT10XX_FT_CONF 0x60
/** \brief Binary data file. */
#define CRD_UT10XX_FT_BIN 0x70
/** \brief Unknown type file. */
#define CRD_UT10XX_FT_UNKN 0xF0
/** \} */

/**
 * \name File type masks
 * \{
 */
 
/** \brief Printable file mask. */
#define CRD_UT10XX_FTM_PRINT 0x01
/** \} */

/**
 * \name File positions
 * \{
 */

/** \brief End of file. */
#define CRD_UT10XX_FPOS_END -1
/** \brief Begin of file. */
#define CRD_UT10XX_FPOS_BEGIN -2
/** \brief Current file position. */
#define CRD_UT10XX_FPOS_CUR -3
/** \} */

/**
 * \brief File structure.
 */
typedef struct CRD_UT10XX_file_t
{
	/** \brief Pointer to device structure. */
	CRD_UT10XX_t *ut;
	/** \brief File number. */
	int num;
	/** \brief File type. */
	int type;
	/** \brief File size in octets. */
	int size;
	/** \brief File name. */
	char name[29];
	/** \brief Table of 32 sectors with 128 bytes each. */
	int sectors[32];
	/** \brief Contents of the file. */
	char contents[4096];
	/** \brief Actual position of file pointer. */
	int fpos;
	/** \brief Variable is not 0 if file contents where flushed. */
	int flushed;
}
CRD_UT10XX_file_t;

/**
 * \brief Actualize file list in \ref CRD_UT10XX_t structure.
 *
 * \param[in,out] ut -
 *   device for which file list is actualized.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_fs_get_file_list(CRD_UT10XX_t *ut);

/**
 * \brief Clear device file system table.
 *
 * \param[in,out] ut -
 *   device for which file system table is cleared.
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_fs_clear(CRD_UT10XX_t *ut, const char *pin, int pin_id);

/**
 * \brief Read all data stored in file system.
 *
 * Readed string is in ASCII format and can be printed using %s specifier 
 * in standard C library.
 *
 * \param[in,out] ut -
 *   device which public memory is readed.
 * \param[out] str -
 *   table of characters for scanned data (it should be about 4 times lerger 
 *   than avaliable public memory).
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_fs_scan(CRD_UT10XX_t *ut, char *str);

/**
 * \brief Open file with given name.
 *
 * This function open file borth to read and write operations. If file with 
 * name \a file_name doesn't exist then it can't be open. 
 *
 * \param[in,out] ut -
 *   device for which file is opened.
 * \param[in] file_name -
 *   name of the file to open.
 *
 * \return 
 *   - pointer to file if operation is done properly,
 *   - zero pointer in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE CRD_UT10XX_file_t* CRD_UT10XX_file_open(CRD_UT10XX_t *ut, const char *file_name);

/**
 * \brief Create file with given name and type.
 *
 * Function checks if file of such file exist in the file system of \a ut. 
 * If not, then file with name \a file_name and type \a type is created 
 * and opened.
 *
 * \param[in,out] ut -
 *   device for which file is created.
 * \param[in] file_name -
 *   name of the file to create.
 * \param[in] type -
 *   type of file.
 *
 * \return 
 *   - pointer to file if operation is done properly,
 *   - zero pointer in other case.
 *
 * \attention 
 *   - To confirm file creation you have to use \ref CRD_UT10XX_file_flush 
 *     function to write all chnges to device.
 *   - It is necesarily to heve open device session.
 */
CRD_DECLARE CRD_UT10XX_file_t* CRD_UT10XX_file_create(CRD_UT10XX_t *ut, const char *file_name, 
	int type);

/**
 * \brief Get file type.
 *
 * \param[in,out] fd -
 *   file descriptor.
 *
 * \return
 *   - nonegative value which represents file type,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_file_type(CRD_UT10XX_file_t *fd);

/**
 * \brief Set printable mask if all characters are printable.
 *
 * \param[in,out] fd -
 *   file descriptor.
 *
 * \return
 *   - 1 if file is printable,
 *   - 0 in other case.
 */
CRD_DECLARE int CRD_UT10XX_file_type_set_printable_mask(CRD_UT10XX_file_t *fd);

/**
 * \brief Change name and type of the file.
 *
 * \param[in,out] fd -
 *   file descriptor.
 * \param[in] new_name -
 *   character string that contains new file name (at most 28 characters).
 * \param[in] type -
 *   definition of file type.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention To confirm file new name you have to use \ref CRD_UT10XX_file_flush 
 *   function to write all chnges to device. Calling rename without flush does 
 *   not introduce any changes in real device file system.
 */
CRD_DECLARE int CRD_UT10XX_file_rename(CRD_UT10XX_file_t *fd, const char *new_name, int type);

/**
 * \brief Set current file position.
 *
 * Function changes position of current file pointer. There are three special 
 * file positions
 *  - \ref CRD_UT10XX_FPOS_END (end of the file),
 *  - \ref CRD_UT10XX_FPOS_BEGIN (begin of the file),
 *  - \ref CRD_UT10XX_FPOS_CUR (current position).
 * After call function returns new value of file pointer. This means that call 
 * \c CRD_UT10XX_file_set_fpos(fd,CRD_UT10XX_FPOS_CUR) returns current position 
 * of file pointer.
 *
 * \param[in,out] fd -
 *   file descriptor.
 * \param[in] fpos -
 *   new file position.
 *
 * \return
 *   - nonegative value wchich represents value of file pointer after change,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_file_set_fpos(CRD_UT10XX_file_t *fd, int fpos);

/**
 * \brief Read octets from file.
 *
 * Function reads \a size of octets from \a fd file and copy it to \a buf 
 * buffer. If number of readed octets is smaller than \a size, then file 
 * pointer achived end of file.
 *
 * \param[in,out] fd -
 *   file descriptor.
 * \param[out] buf -
 *   buffer for data.
 * \param[in] size -
 *   number of octets to read.
 *
 * \return
 *   - positive value equal to number of readed octets,
 *   - zero if there is end of this file,
 *   - negative error code in other case.
 */
CRD_DECLARE int CRD_UT10XX_file_read(CRD_UT10XX_file_t *fd, char *buf, int size);

/**
 * \brief Write octets to file.
 *
 * Function writes octets contained in \a buf to file descriptor \a fd.
 *
 * \param[in,out] fd -
 *   file descriptor.
 * \param[in] buf -
 *   buffer with data.
 * \param[in] size -
 *   number of octets to write.
 *
 * \return
 *   - nonegative value equal to number of written octets,
 *   - negative error code in other case.
 *
 * \attention
 *   - Single file can not be larger than 4096 octets.
 *   - This function does not write octets to device (only storage them in 
 *     internal \a fd buffer). To write octets to device there is a need to 
 *     call \ref CRD_UT10XX_file_flush function.
 */
CRD_DECLARE int CRD_UT10XX_file_write(CRD_UT10XX_file_t *fd, const char *buf, int size);

/**
 * \brief Drops file data to device.
 *
 * This function writes all data stored in \a fd to real device. It was introduced 
 * to reduce number of writes to device EEPROM. This idea increases live time of 
 * EEPROM and reduces time (writing to EEPROM is rather slow). It is a good practice 
 * to use this function only before \ref CRD_UT10XX_file_close call.
 *
 * \param[in,out] fd -
 *   file descriptor.
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_file_flush(CRD_UT10XX_file_t *fd, const char *pin, int pin_id);

/**
 * \brief Delete file.
 *
 * \param[in,out] fd -
 *   file descriptor.
 * \param[in] pin -
 *   character string which contains User or SO PIN (at most 8 characters).
 * \param[in] pin_id -
 *   PIN owner identification number. Use \ref CRD_UT10XX_SO_PIN_ID for SO 
 *   and \ref CRD_UT10XX_USER_PIN_ID for User.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_file_delete(CRD_UT10XX_file_t *fd, const char *pin, int pin_id);

/**
 * \brief Close file.
 *
 * Function coses file and removes unflushed changes.
 *
 * \param[in,out] fd -
 *   file descriptor.
 *
 * \return
 *   - nonegative value if operation is done properly,
 *   - negative error code in other case.
 *
 * \attention It is necesarily to heve open device session.
 */
CRD_DECLARE int CRD_UT10XX_file_close(CRD_UT10XX_file_t *fd);

/** \} */

/** \} */

#ifdef __cplusplus
};
#endif

#endif /* ifndef __CRD_UT10XX_H */

/**
 * \file common.h
 * \brief Definitions common for device and host software
 */
#ifndef __CRD_UT10XX_COMMON_H
#define __CRD_UT10XX_COMMON_H

/**
 * \defgroup common_group Common definitions
 * \brief Definitions common for device and host software.
 *
 * \{
 */

 
/**
 * \name Supported ECC domains
 * \{
 */

/**
 * \brief Domain over 160-bits prime field defined in SEC2.
 *
 * \code
 *   secp160r1 OBJECT IDENTIFIER ::= { 
 *     iso(1) identified-organization(3) certicom(132) curve(0) 8 
 *   }
 * \endcode
 */
#define SECP160R1 1600

/**
 * \brief Domain over 192-bits prime field defined in SEC2.
 *
 * \code
 *   secp192r1 OBJECT IDENTIFIER ::= { 
 *     iso(1) member-body(2) us(840) 10045 curves(3) prime(1) 1
 *   }
 * \endcode
 */
#define SECP192R1 1920

/**
 * \brief Domain over 224-bits prime field defined in SEC2.
 *
 * \code
 *   secp224r1 OBJECT IDENTIFIER ::= { 
 *     iso(1) identified-organization(3) certicom(132) curve(0) 33 
 *   }
 * \endcode
 */
#define SECP224R1 2240

/**
 * \brief Domain over 256-bits prime field defined in SEC2.
 *
 * \code
 *   secp256r1 OBJECT IDENTIFIER ::= { 
 *     iso(1) member-body(2) us(840) 10045 curves(3) prime(1) 7
 *   }
 * \endcode
 */
#define SECP256R1 2560

/** \} */


/**
 * \name PIN identifiers
 * \{
 */
 
/** \brief Security Officer (SO) PIN identifier. */
#define SO_PIN_ID 0
/** \brief User PIN identified. */
#define USER_PIN_ID 1
/** \} */


/**
 * \name Commands
 * \{
 */

/** \brief Number of command bit set to 1 if it needs SO pin. */
#define SOPC 7
/** \brief Number of command bit set to 1 if it needs User pin. */
#define URPC 6
/** \brief Number of command bit set to 1 if it needs SO or User pin. */
#define SUPC 5

/**
 * \brief This command initialises device.
 *
 * It is avaliable only if device was not initialised
 * before or device was erased.
 */
#define CMD_DEV_INIT 0x00

/**
 * \brief This command configures device.
 *
 * It is obligatory if you want to use other commands.
 * During this process the following informations are set:
 *  - device information and configuration bits,
 *  - device name,
 *  - limits for wrong PIN,
 *  - PIN for user and security officer.
 */
#define CMD_DEV_CONF 0x01

/**
 * \brief This command reads information about device.
 */
#define CMD_READ_DEV_INFO 0x02

/**
 * \brief This command reads information about key and contents of public key.
 */
#define CMD_READ_KEY_INFO 0x03

/**
 * \brief This command reads object from public memory.
 */
#define CMD_READ_PUBLIC_OBJ 0x04

/**
 * \brief This command reads random bits generated by device.
 */
#define CMD_RBG_GENERATE_BITS 0x05

/**
 * \brief This command unlock device.
 */
#define CMD_DEV_UNLOCK (0x06 | (1 << SOPC))

/**
 * \brief This command cleans all protected memory.
 */
#define CMD_DEV_ERASE (0x07 | (1 << SOPC))

/**
 * \brief This command changes Security Officer PIN.
 */
#define CMD_CHANGE_SO_PIN (0x08 | (1 << SOPC))

/**
 * \brief This command generates signature in ECDSA scheme.
 */
#define CMD_ECDSA_SIGN (0x09 | (1 << URPC))

/**
 * \brief This command computes shared info in ECDH or ECIES scheme. 
 */
#define CMD_ECDH_KEY_AGREEMENT (0x0a | (1 << URPC))

/**
 * \brief This command changes User PIN.
 */
#define CMD_CHANGE_USER_PIN (0x0b | (1 << SUPC))

/**
 * \brief This command generates new User key.
 */
#define CMD_GENERATE_KEY (0x0c | (1 << SUPC))

/**
 * \brief This command removs key from protrcted memory.
 */
#define CMD_DELETE_KEY (0x0d | (1 << SUPC))

/**
 * \brief This command writes object to public memory.
 */
#define CMD_WRITE_PUBLIC_OBJ (0x0e | (1 << SUPC))

/**
 * \brief This command checks if PIN is correct.
 */
#define CMD_TRY_PIN (0x0f | (1 << SUPC))

/** \} */


/**
 * \name Device information and configuration bits
 * \{
 */

/** \brief Set to 1 if device is initialised. */
#define INIT_BIT 0
/** \brief Set to 1 if device is configured. */
#define CONF_BIT 1
/** \brief Set to 1 if device is locked. */
#define LOCK_BIT 2
/** \brief Set to 1 if erasing is in progress. */
#define EINP_BIT 3
/** \brief Set to 1 if change of user pin is not allowed. */
#define UPCNA_BIT 4
/** \} */


/**
 * \name Key information and configuration bits
 * \{
 */

/** \brief Set to 1 if key is generated. */
#define GEN_BIT 0
/** \} */


/**
 * \name Octets of EEPROM memory structures
 * \{
 */

/** \brief Octets of device bit information and configuration. */
#define DEV_INFO_OCTETS 2
/** \brief Octets of device name structure. */
#define DEV_NAME_OCTETS 10
/** \brief Octets of PIN trys number. */
#define PIN_TRYS_OCTETS 1
/** \brief Octets of PIN trys limit number. */
#define PIN_LIMIT_OCTETS 1
/** \brief Octets of PIN representation. */
#define PIN_OCTETS 8
/** \brief Octets of key information structure. */
#define KEY_INFO_OCTETS 24
/** \brief Octets of maximum public key structure. */
#define KEY_PUBLIC_OCTETS 64
/** \brief Octets of maximum private key structure. */
#define KEY_PRIVATE_OCTETS 32
/** \brief Octets of single public object. */
#define PUBLIC_OBJ_OCTETS 64
/** \} */

/**
 * \name Host frame headers
 * \{
 */

/** \brief Frame with host command. */
#define HF_CMD 0x00
/** \brief Maximum size of host command frame body. */
#define HF_CMD_BODY_MAX 128
/** \} */

/**
 * \name Device frame headers
 * \{
 */

/** \brief Frame with error. */
#define DF_ERR 0x80
/** \brief Size of device error frame body. */
#define DF_ERR_BODY 1
/** \brief Frame with device data. */
#define DF_DTE 0x83
/** \brief Maximum size of device data frame body. */
#define DF_DTE_BODY_MAX 128
/** \} */


/**
 * \name Device error codes
 * \{
 */
 
/** \brief There is no such command. */
#define ERR_R_WRONG_CMD 0
/** \brief Command is not allowed in this state of device. */
#define ERR_R_CMD_NOT_ALLOWED 1
/** \brief Wrong PIN during pin verification process. */
#define ERR_R_WRONG_PIN 2
/** \brief Point passed to ECDH shared info primitive is not on supported curve. */
#define ERR_R_WRONG_EC_POINT 3
/** \brief There is no such key. */
#define ERR_R_NO_KEY 4
/** \brief Argument passed to device is out of range. */
#define ERR_R_ARG_RANGE 5
/** \brief Wrong frame syntax. */
#define ERR_R_FRAME_SYNTAX 6
/** \brief Length of frame is out of protocol specified range. */
#define ERR_R_FRAME_RANGE 7
/** \} */

/** \} */

#endif /* __CRD_UT10XX_COMMON_H */

/*
 *
 */
 
#ifndef __CP210X_H
#define __CP210X_H

#ifndef NO_INCLUDE_LOCL
#include "crd_ut10xx.h"
#endif

#ifdef __cplusplus
extern "C"
{
#endif

/**
 * \defgroup low_level_interface_group Low level interfaces.
 * \brief Module contains basic interfaces to CRD-UT10XX hardware components.
 * \{
 */

/** \brief Communication time out in miliseconds. */
#define CRD_UT10XX_TIME_OUT 7000
/** \brief Communication long time out in miliseconds (for initialization only). */
#define CRD_UT10XX_LONG_TIME_OUT 40000

/**
 * \defgroup cp210x_interface_group CP210X low level interface.
 * \brief Module contains basic interface to CP210x USB-UART bridge (mounted in CRD-UT10XX).
 * \{
 */

/** \brief Vendor ID for CP210X (Silicon Labs). */
#define CP210X_VENDOR_ID 0x10c4
/** \brief Product ID for CRD-UT10XX (CMM Sigma). */
/*#define CP210X_PRODUCT_ID 0xea60*/
#define CP210X_PRODUCT_ID 0x82e5

/**
 * \name USB endpoints.
 * \{
 */

/** \brief CP210X input bulk endpoint. */
#define CP210X_IN_BULK_EP 0x81
/** \brief CP210X output bulk endpoint. */
#define CP210X_OUT_BULK_EP 0x01
/** \} */

/* 
 * Config request types.
 */
#define CP210X_REQTYPE_HOST_TO_DEVICE 0x41
#define CP210X_REQTYPE_DEVICE_TO_HOST 0xc1

/* 
 * Config SET requests. To GET, add 1 to the request number.
 */
#define CP210X_UART 0x00 /* Enable / Disable */
#define CP210X_BAUDRATE 0x01 /* (BAUD_RATE_GEN_FREQ / baudrate) */
#define CP210X_BITS 0x03 /* 0x(0)(databits)(parity)(stopbits) */
#define CP210X_BREAK 0x05 /* On / Off */
#define CP210X_CONTROL 0x07 /* Flow control line states */
#define CP210X_MODEMCTL 0x13 /* Modem controls */
#define CP210X_CONFIG_6 0x19 /* 6 bytes of config data ??? */

/* 
 * CP210X_UART 
 */
#define CP210X_UART_DISABLE 0x0000
#define CP210X_UART_ENABLE 0x0001

/* 
 * CP210X_BAUDRATE 
 */
#define CP210X_BAUD_RATE_GEN_FREQ 0x384000

/* 
 * CP210X_BITS 
 */
#define CP210X_BITS_DATA_MASK 0x0f00
#define CP210X_BITS_DATA_5 0x0500
#define CP210X_BITS_DATA_6 0x0600
#define CP210X_BITS_DATA_7 0x0700
#define CP210X_BITS_DATA_8 0x0800
#define CP210X_BITS_DATA_9 0x0900

#define CP210X_BITS_PARITY_MASK 0x00f0
#define CP210X_BITS_PARITY_NONE 0x0000
#define CP210X_BITS_PARITY_ODD 0x0010
#define CP210X_BITS_PARITY_EVEN 0x0020
#define CP210X_BITS_PARITY_MARK 0x0030
#define CP210X_BITS_PARITY_SPACE0 0x0040

#define CP210X_BITS_STOP_MASK 0x000f
#define CP210X_BITS_STOP_1 0x0000
#define CP210X_BITS_STOP_1_5 0x0001
#define CP210X_BITS_STOP_2 0x0002

/* 
 * CP210X_BREAK 
 */
#define CP210X_BREAK_ON 0x0000
#define CP210X_BREAK_OFF 0x0001

/* 
 * CP210X_CONTROL 
 */
#define CP210X_CONTROL_DTR 0x0001
#define CP210X_CONTROL_RTS 0x0002
#define CP210X_CONTROL_CTS 0x0010
#define CP210X_CONTROL_DSR 0x0020
#define CP210X_CONTROL_RING 0x0040
#define CP210X_CONTROL_DCD 0x0080
#define CP210X_CONTROL_WRITE_DTR 0x0100
#define CP210X_CONTROL_WRITE_RTS 0x0200

extern int CP210X_CRD_UT10XX_open(CRD_UT10XX_t *ut);
extern int CP210X_CRD_UT10XX_close(CRD_UT10XX_t *ut);
extern int CP210X_CRD_UT10XX_read(CRD_UT10XX_t *ut, unsigned char *buf, int octets, int timeout_ms);
extern int CP210X_CRD_UT10XX_write(CRD_UT10XX_t *ut, const unsigned char *buf, int octets);

/** \} */

/** \} */

#ifdef __cplusplus
};
#endif

#endif /* ifndef __CP210X_H */

/*
 *
 */
 
#ifndef __CRD_UT10XX_ERROR_H
#define __CRD_UT10XX_ERROR_H

#ifdef __cplusplus
extern "C" 
{
#endif

#define STR(x) #x
#define XSTR(x) STR(x)

#define ERROR_push_msg(e, m) \
	ERROR_push(e, sizeof(e), __FILE__, __func__, XSTR(__LINE__), m)

extern int ERROR_count(const char *err);
extern int ERROR_push(char *err, int size, const char *file, const char *func, 
	const char *line, const char *msg);
extern const char* ERROR_pop(char *err);

#ifdef __cplusplus
};
#endif

#endif /* ifndef __CRD_UT10XX_ERROR_H */

/*
 *
 */
 
#ifndef __SEC_H
#define __SEC_H

#ifdef __cplusplus
extern "C" 
{
#endif

extern const unsigned char SECP160R1_named[7];
extern const unsigned char SECP160R1_specified[178];
extern const unsigned char SECP160R1_subject_public_key_info_alg[18];

extern const unsigned char SECP192R1_named[10];
extern const unsigned char SECP192R1_specified[202];
extern const unsigned char SECP192R1_subject_public_key_info_alg[21];

extern const unsigned char SECP224R1_named[7];
extern const unsigned char SECP224R1_specified[226];
extern const unsigned char SECP224R1_subject_public_key_info_alg[18];

extern const unsigned char SECP256R1_named[10];
extern const unsigned char SECP256R1_specified[250];
extern const unsigned char SECP256R1_subject_public_key_info_alg[21];

#ifdef __cplusplus
};
#endif

#endif

/*
 *
 */

#ifndef NO_INCLUDE_LOCL
#include "sec.h"
#include "error.h"
#include "common.h"
#include "cp210x.h"
#include "crd_ut10xx.h"
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef ERROR
#undef ERROR
#endif

#define ERROR(ut, m) ERROR_push_msg((ut)->err, m)

static volatile int connected_apps = 0;

/** \brief Table of supported ECC domains. */
static const CRD_UT10XX_ecc_params_t CRD_UT10XX_ecc_params[] = 
{
	/*
	 * int id;
	 * int field_octets;
	 * int order_octets;
	 * const char *name;
	 * int asn1_named_octets;
	 * const unsigned char *asn1_named;
	 * const unsigned char *asn1_named_alg;
	 * int asn1_specified_octets;
	 * const unsigned char *asn1_specified;
	 */
	/** \brief Paramethers defined in SEC-2 as secp160r1. */
	{
		SECP160R1,
		20,
		21,
		"secp160r1",
		sizeof(SECP160R1_named),
		SECP160R1_named,
		SECP160R1_subject_public_key_info_alg,
		sizeof(SECP160R1_specified),
		SECP160R1_specified
	},
	/** \brief Paramethers defined in SEC-2 as secp192r1. */
	{
		SECP192R1,
		24,
		24,
		"secp192r1",
		sizeof(SECP192R1_named),
		SECP192R1_named,
		SECP192R1_subject_public_key_info_alg,
		sizeof(SECP192R1_specified),
		SECP192R1_specified
	},
	/** \brief Paramethers defined in SEC-2 as secp224r1. */
	{
		SECP224R1,
		28,
		28,
		"secp224r1",
		sizeof(SECP224R1_named),
		SECP224R1_named,
		SECP224R1_subject_public_key_info_alg,
		sizeof(SECP224R1_specified),
		SECP224R1_specified
	},
	/** \brief Paramethers defined in SEC-2 as secp256r1. */
	{
		SECP256R1,
		32,
		32,
		"secp256r1",
		sizeof(SECP256R1_named),
		SECP256R1_named,
		SECP256R1_subject_public_key_info_alg,
		sizeof(SECP256R1_specified),
		SECP256R1_specified
	}
};

const CRD_UT10XX_ecc_params_t* 
CRD_UT10XX_get_ecc_params(int id)
{
	const CRD_UT10XX_ecc_params_t* params = CRD_UT10XX_ecc_params;
	int total = sizeof(CRD_UT10XX_ecc_params) / sizeof(CRD_UT10XX_ecc_params_t);
	int i;
	
	for (i = 0; i < total; i++)
	{
		if (params->id == id)
			return (params);
		
		params++;
	}
	
	return (0);
}

/**
 * \brief List of devices attached to host.
 */
typedef struct CRD_UT10XX_list_t
{
	/** \brief Total number of attached devices. */
	int total;
	/** \brief Pointer to first device. */
	CRD_UT10XX_t *begin;
	/** \brief Pointer to last device. */
	CRD_UT10XX_t *end;
} 
CRD_UT10XX_list_t;

/** 
 * \brief Global list of devices attached to host. 
 */
static CRD_UT10XX_list_t CRD_UT10XX_devices = 
{
	.total = 0,
	.begin = 0,
	.end = 0
};

int
CRD_UT10XX_lib_init(void)
{
	if (connected_apps++ == 0)
		usb_init();
	
	return (0);
}

int
CRD_UT10XX_lib_finish(void)
{
	if (--connected_apps == 0)
	{
		/* Delete all devices. */
		while (CRD_UT10XX_devices.begin)
		{
			CRD_UT10XX_devices.end = CRD_UT10XX_devices.begin->next;
			memset(CRD_UT10XX_devices.begin, 0, sizeof(CRD_UT10XX_t));
			free(CRD_UT10XX_devices.begin);
			CRD_UT10XX_devices.begin = CRD_UT10XX_devices.end;
		}
	}
	
	return (0);
}

int
CRD_UT10XX_lib_find_devices(void)
{
	CRD_UT10XX_t *ut;
	struct usb_bus *bus;
	struct usb_device *dev;
	
	/* Clear total number of attached devices. */
	CRD_UT10XX_devices.total = 0;
	
	/* Clear present flag in all devices on list. */
	for (ut = CRD_UT10XX_devices.begin; ut; ut = ut->next)
		ut->present = 0;
	
	/* Initialization of LibUSB data structures. */
	usb_find_busses();
	usb_find_devices();
	
	/* Search all buses and devices. */
	for (bus = usb_get_busses(); bus; bus = bus->next)
	{
		for (dev = bus->devices; dev; dev = dev->next)	
		{
			/* Looking for devices with specific VID and PID. */
			if ((dev->descriptor.idVendor == CP210X_VENDOR_ID) 
				&& (dev->descriptor.idProduct == CP210X_PRODUCT_ID))
			{
				/* Increase number of attached devices. */
				CRD_UT10XX_devices.total += 1;
				
				/* Looking for device in list. */
				for (ut = CRD_UT10XX_devices.begin; ut; ut = ut->next)
				{
					if (ut->hw->devnum == dev->devnum)
					{
						/* Check as present. */
						ut->present = 1;
						break;
					}
				}
				
				/* 
				 * If new device was attached then alloc memory and fill 
				 * its data structure.
				 */
				if ( (ut == 0) && ((ut = malloc(sizeof(CRD_UT10XX_t))) != 0) )
				{
					memset(ut, 0, sizeof(CRD_UT10XX_t));
					/* Check as present. */
					ut->present = 1;
					/* Assign pointer to LibUSB device structure. */
					ut->hw = dev;
					/* Read USB device product string and serial number. */
					memset(ut->product_string, 0, sizeof(ut->product_string));
					memset(ut->serial_number, 0, sizeof(ut->serial_number));
					ut->handle = usb_open(ut->hw);
					usb_get_string_simple(ut->handle, ut->hw->descriptor.iProduct, 
						ut->product_string, sizeof(ut->product_string) - 1);
					usb_get_string_simple(ut->handle, ut->hw->descriptor.iSerialNumber, 
						ut->serial_number, sizeof(ut->serial_number) - 1);
#					ifndef WIN32
					usb_reset(ut->handle);
#					endif
					usb_close(ut->handle);
					ut->handle = 0;
					/* Assign Basic interface functions. */
					ut->open = CP210X_CRD_UT10XX_open;
					ut->close = CP210X_CRD_UT10XX_close;
					ut->read = CP210X_CRD_UT10XX_read;
					ut->write = CP210X_CRD_UT10XX_write;
					/* Number of possible files is constant and equal 16. */
					ut->files_no = 16;
					
					/* Attach new device to the list. */
					if (CRD_UT10XX_devices.begin)
						CRD_UT10XX_devices.end->next = ut;
					
					else
						CRD_UT10XX_devices.begin = ut;
					
					CRD_UT10XX_devices.end = ut;
				}
			}
		}
	}
	
	/* Remove devices which are not present. */
	while (CRD_UT10XX_devices.begin && !CRD_UT10XX_devices.begin->present)
	{
		CRD_UT10XX_devices.end = CRD_UT10XX_devices.begin->next;
		memset(CRD_UT10XX_devices.begin, 0, sizeof(CRD_UT10XX_t));
		free(CRD_UT10XX_devices.begin);
		CRD_UT10XX_devices.begin = CRD_UT10XX_devices.end;
	}
	
	CRD_UT10XX_devices.end = CRD_UT10XX_devices.begin;
	
	while (CRD_UT10XX_devices.end && CRD_UT10XX_devices.end->next)
	{
		if (CRD_UT10XX_devices.end->next->present)
		{
			CRD_UT10XX_devices.end = CRD_UT10XX_devices.end->next;
		}
		else
		{
			ut = CRD_UT10XX_devices.end->next;
			CRD_UT10XX_devices.end->next = ut->next;
			memset(ut, 0, sizeof(CRD_UT10XX_t));
			free(ut);
		}
	}
	
	/* Return total number of devices attached to host. */
	return (CRD_UT10XX_devices.total);
}

int 
CRD_UT10XX_lib_actualize_devices(void)
{
	CRD_UT10XX_t *ut;
	int key_num;
	
	int status;
	
	/* 
	 * For all devices attached to host read information about them 
	 * and their keys.
	 */
	for (ut = CRD_UT10XX_devices.begin; ut; ut = ut->next)
	{
		status = CRD_UT10XX_session_open(ut);
		
		if (status < 0)
			return (status);
		
		status = CRD_UT10XX_cmd_read_dev_info(ut);
		
		if (status < 0)
		{
			CRD_UT10XX_session_close(ut);
			return (status);
		}
		
		if ((ut->info.info & (1 << CONF_BIT)) 
			&& !(ut->info.info & (1 << LOCK_BIT)))
		{
			for (key_num = 0; key_num < ut->info.keys_num; key_num++)
			{
				status = CRD_UT10XX_cmd_read_key_info(ut, key_num);
				
				if (status < 0)
				{
					CRD_UT10XX_session_close(ut);
					return (status);
				}
			}
			
			status = CRD_UT10XX_fs_get_file_list(ut);
			
			if (status < 0)
			{
				CRD_UT10XX_session_close(ut);
				return (status);
			}
		}
		
		status = CRD_UT10XX_session_close(ut);
		
		if (status < 0)
			return (status);
	}
	
	return (0);
}

const char *
CRD_UT10XX_lib_error_desc(int err)
{
	const char *undef = 
		"undefined error (error specification is not compleat)";
	const char *lock = 
		"session could not be open (perhaps device was locked by other "
		"thread or application)";
	const char *unlock = 
		"session could not be close because it is not open";
	const char *closed = 
		"session is closed";
	const char *receive = 
		"message receiving error";
	const char *transmit = 
		"message transmition error";
	const char *toosmallbuf = 
		"buffer is too small (can not write everything)";
	const char *toolargebuf = 
		"buffer is too large (can not read everything)";
	const char *setup = 
		"unable to configure device interface";
	const char *wrongkey = 
		"key number is not supported by device";
	const char *notsupported = 
		"device is not supported by this software";
	const char *argrange = 
		"arguments are out of range";
	const char *notinit = 
		"not initialized parameter";
	const char *dev_wrongcmd = 
		"wrong command sended to device";
	const char *dev_cmdnotallowed = 
		"command is not allowed in this state of device";
	const char *dev_wrongpin =
		"wrong PIN, command not allowed";
	const char *dev_wrongecpoint = 
		"point passed to ECDH shared info primitive is not on curve";
	const char *dev_nokey = 
		"key you are trying to use is not generated";
	const char *dev_argrange = 
		"arguments sended to device are out of range";
	const char *dev_framesyntax = 
		"wrong communication frame syntax";
	const char *dev_framerange = 
		"wrong size of communication frame";
	const char *unknown = 
		"unknown error";
		
	switch (err)
	{
		case ERR_CRD_UT10XX_UNDEF:
			return (undef);
		
		case ERR_CRD_UT10XX_LOCK:
			return (lock);
		
		case ERR_CRD_UT10XX_UNLOCK:
			return (unlock);
		
		case ERR_CRD_UT10XX_SESSION_CLOSED:
			return (closed);
		
		case ERR_CRD_UT10XX_RECEIVE:
			return (receive);
			
		case ERR_CRD_UT10XX_TRANSMIT:
			return (transmit);
			
		case ERR_CRD_UT10XX_TOO_SMALL_BUF:
			return (toosmallbuf);
			
		case ERR_CRD_UT10XX_TOO_LARGE_BUF:
			return (toolargebuf);
			
		case ERR_CRD_UT10XX_SET_UP:
			return (setup);
			
		case ERR_CRD_UT10XX_WRONG_KEY:
			return (wrongkey);
			
		case ERR_CRD_UT10XX_NOT_SUPPORTED:
			return (notsupported);
		
		case ERR_CRD_UT10XX_ARG_RANGE:
			return (argrange);
		
		case ERR_CRD_UT10XX_NOT_INIT:
			return (notinit);
			
		case ERR_CRD_UT10XX_DEV_WRONG_CMD:
			return (dev_wrongcmd);
			
		case ERR_CRD_UT10XX_DEV_CMD_NOT_ALLOWED:
			return (dev_cmdnotallowed);
			
		case ERR_CRD_UT10XX_DEV_WRONG_PIN:
			return (dev_wrongpin);
			
		case ERR_CRD_UT10XX_DEV_WRONG_EC_POINT:
			return (dev_wrongecpoint);
		
		case ERR_CRD_UT10XX_DEV_NO_KEY:
			return (dev_nokey);
			
		case ERR_CRD_UT10XX_DEV_ARG_RANGE:
			return (dev_argrange);
			
		case ERR_CRD_UT10XX_DEV_FRAME_SYNTAX:
			return (dev_framesyntax);
			
		case ERR_CRD_UT10XX_DEV_FRAME_RANGE:
			return (dev_framerange);
	}
		
	return (unknown);
}

const char*
CRD_UT10XX_lib_detail_error_desc(const CRD_UT10XX_t *ut)
{
	return (ut->err);
}

int 
CRD_UT10XX_lib_detail_error_desc_to_file(const CRD_UT10XX_t *ut, const char *fmt, 
	FILE *fd)
{
	char err_stack[sizeof(ut->err)];
	const char *err;
	
	/* Copy error stack. */
	strcpy(err_stack, ut->err);
	
	/* Pritn all errors stored on stack to given file. */
	while ( (err = ERROR_pop(err_stack)) != 0 )
		fprintf(fd, fmt, err);
	
	return (0);
}

int
CRD_UT10XX_session_open(CRD_UT10XX_t *ut)
{
	int status;
	
	/* to be done - OPEN_CRITICAL_SECTION */
	if (ut->session_is_open)
		return (ERR_CRD_UT10XX_LOCK);
	
	memset(ut->err, 0, sizeof(ut->err));
	
	if ( (status = (*ut->open)(ut)) < 0 )
	{
		ERROR(ut, "could not open device");
		return (status);
	}
	
	ut->session_is_open = 1;
	/* to be done - CLOSE_CRITICAL_SECTION */
	
	return (0);
}

int
CRD_UT10XX_session_close(CRD_UT10XX_t *ut)
{
	int status;
	
	/* to be done - OPEN_CRITICAL_SECTION */
	if (!ut->session_is_open)
		return (ERR_CRD_UT10XX_UNLOCK);
	
	if ( (status = (*ut->close)(ut)) < 0 )
	{
		ERROR(ut, "could not close device");
		return (status);
	}
	
	ut->session_is_open = 0;
	/* to be done - CLOSE_CRITICAL_SECTION */
	
	return (0);
}

CRD_UT10XX_t*
CRD_UT10XX_get_first(void)
{
	return (CRD_UT10XX_devices.begin);
}

CRD_UT10XX_t*
CRD_UT10XX_find_by_index(int idx)
{
	CRD_UT10XX_t *ut = 0;
	
	/* Find pointer to device with given index. */
	if (idx < CRD_UT10XX_devices.total)
	{
		ut = CRD_UT10XX_devices.begin;
		while (idx--)
			ut = ut->next;
	}
	
	return (ut);
}

CRD_UT10XX_t*
CRD_UT10XX_find_by_name(const char *name)
{
	CRD_UT10XX_t *ut = CRD_UT10XX_devices.begin;
	
	/* Find pointer to device with given name. */
	while (ut)
	{
		if (strncmp(name, ut->info.name, sizeof(ut->info.name)) == 0)
			break;
			
		ut = ut->next;
	}
	
	return (ut);
}

CRD_UT10XX_t*
CRD_UT10XX_find_by_serial_number(const char *serial_number)
{
	CRD_UT10XX_t *ut = CRD_UT10XX_devices.begin;
	
	/* Find pointer to device with given name. */
	while (ut)
	{
		if (strncmp(serial_number, ut->serial_number, sizeof(ut->serial_number)) == 0)
			break;
			
		ut = ut->next;
	}
	
	return (ut);
}

int
CRD_UT10XX_find_index_of(CRD_UT10XX_t *ut)
{
	CRD_UT10XX_t *t = CRD_UT10XX_devices.begin;
	int idx = 0;
	
	while (t)
	{
		if (t == ut)
			break;
		
		t = t->next;
		idx++;
	}
	
	if (!t)
		idx = -1;
	
	return (idx);
}

int 
CRD_UT10XX_find_free_key(CRD_UT10XX_t *ut)
{
	int key_num;
	
	/* Searching index of free key. */
	for (key_num = 0; key_num < ut->info.keys_num; key_num++)
	{
		if ( (((ut->keys[key_num].info)) & (1 << GEN_BIT)) == 0 )
			return (key_num);
	}
	
	ERROR(ut, "there is no free key");
	return (-1);
}

int 
CRD_UT10XX_find_key_by_name(CRD_UT10XX_t *ut, const char *name)
{
	int key_num;
	
	/* Seaching index of key eith given name. */
	for (key_num = 0; key_num < ut->info.keys_num; key_num++)
	{
		if ( strcmp(name, ((ut->keys) + key_num)->name) == 0 )
			return (key_num);
	}
	
	ERROR(ut, "there is no key of given name");
	return (-1);
}

int
CRD_UT10XX_subject_public_key_info(const CRD_UT10XX_key_info_t *key, char *key_info, 
	int key_info_octets)
{
	const CRD_UT10XX_ecc_params_t *params = key->ecc_params;
	unsigned char *ptr = (unsigned char *)key_info;
	
	if (ptr == 0)
		return (8 + params->asn1_named_alg[1] + 2*params->field_octets);
	
	if (key_info_octets < 8 + params->asn1_named_alg[1] + 2*params->field_octets)
		return (ERR_CRD_UT10XX_TOO_SMALL_BUF);
	
	/*
	 * Create DER encoding of EC public key.
	 */
	
	/* SubjectPublicKeyInfo SEQUENCE */
	ptr[0] = 0x30;
	ptr[1] = 6 + params->asn1_named_alg[1] + 2*params->field_octets;
	/* algorithm AlgorithmIdentifier */
	memcpy(ptr + 2, params->asn1_named_alg, 2 + params->asn1_named_alg[1]);
	/* subjectPublicKey BIT STRING */
	ptr[4 + ptr[3]] = 0x03;
	ptr[5 + ptr[3]] = 2 + 2*params->field_octets;
	ptr[6 + ptr[3]] = 0x00;
	ptr[7 + ptr[3]] = 0x04;
	memcpy(key_info + 8 + ptr[3], key->pub_x, params->field_octets);
	memcpy(key_info + 8 + ptr[3] + params->field_octets, key->pub_y, 
		params->field_octets);
	
	return (2 + ptr[1]);
}

/**
 * \brief Single pass of communication protocol.
 *
 * \param[in,out] ut -
 *   device.
 * \param[in] out -
 *   buffer with data to send to device.
 * \param[in] out_octets - 
 *   number of data octets in \a out buffer.
 * \param[out] in -
 *   buffer for data received from device.
 * \param[in] in_octets -
 *   size of \a in buffer in octets.
 *
 * \return
 *   - nonegative number of received octets if everything work properly,
 *   - negative error code in other case.
 */
static int
CRD_UT10XX_single_pass(CRD_UT10XX_t *ut, const unsigned char *out, int out_octets, 
	unsigned char *in, int in_octets)
{
	int timeout = CRD_UT10XX_TIME_OUT;
	int status;
	
	if (out[1] == CMD_DEV_INIT)
		timeout = CRD_UT10XX_LONG_TIME_OUT;
	
	/* to be done - OPEN_CRITICAL_SECTION */
	if (!ut->session_is_open)
		return (ERR_CRD_UT10XX_SESSION_CLOSED);
	/* to be done - CLOSE_CRITICAL_SECTION */
	
	if ( (status = (*ut->write)(ut, out, out_octets)) < 0 )
	{
		(*ut->close)(ut);
		ERROR(ut, "could not write to device");
		return (status);
	}
	
	if ( (status = (*ut->read)(ut, in, in_octets, timeout)) < 0 )
	{
		(*ut->close)(ut);
		ERROR(ut, "could not read from device");
		return (status);
	}
	
	if ( (status == 2) && (in[0] == DF_ERR) )
	{
		ERROR(ut, "host receive error frame from device");
		return (ERR_CRD_UT10XX_DEV - in[1]);
	}
	
	return (status);
}

int 
CRD_UT10XX_cmd_dev_init(CRD_UT10XX_t *ut)
{
	unsigned char out[2];
	unsigned char in[2];
	int status;
	
	out[0] = HF_CMD;
	out[1] = CMD_DEV_INIT;
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_dev_conf(CRD_UT10XX_t *ut)
{
	unsigned char out[34];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_DEV_CONF;
	out[2] = (ut->conf.info) & (1 << UPCNA_BIT);
	out[3] = 0;
	strncpy((char *)out + 4, ut->conf.name, 10);
	out[14] = 0;
	out[15] = 0;
	out[16] = (ut->conf.so_pin_limit) & 0xff;
	out[17] = (ut->conf.user_pin_limit) & 0xff;
	memcpy(out + 18, ut->conf.so_pin, 8);
	memcpy(out + 26, ut->conf.user_pin, 8);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	memset(&ut->conf, 0, sizeof(ut->conf));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_read_dev_info(CRD_UT10XX_t *ut)
{
	unsigned char out[2];
	unsigned char in[25];
	int status;
	int octets;
	
	out[0] = HF_CMD;
	out[1] = CMD_READ_DEV_INFO;
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	octets = status;
	
	if (octets != sizeof(in))
	{
		ERROR(ut, "received frame syntax error");
		return (ERR_CRD_UT10XX_RECEIVE);
	}
	
	ut->info.info = in[1];
	strncpy(ut->info.name, (char *)in + 3, 10);
	ut->info.name[10] = '\0';
	ut->info.so_pin_trys = in[13];
	ut->info.user_pin_trys = in[14];
	ut->info.so_pin_limit = in[15];
	ut->info.user_pin_limit = in[16];
	ut->info.main_ver = in[17];
	ut->info.sub_ver = in[18];
	ut->info.public_mem = 256 * (int)in[19] + (int)in[20];
	ut->info.public_objs = ut->info.public_mem / PUBLIC_OBJ_OCTETS;
	ut->info.keys_num = in[21];
	ut->info.ecc_num = in[22];
	ut->info.ecc_params = CRD_UT10XX_get_ecc_params(256 * (int)in[23] + 
		(int)in[24]);
	
	if ( !ut->info.ecc_params )
	{
		ERROR(ut, "received ECC domain paramethers are not supported");
		return (ERR_CRD_UT10XX_NOT_SUPPORTED);
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_read_key_info(CRD_UT10XX_t *ut, int key_num)
{
	unsigned char out[3];
	unsigned char in[DF_MAX];
	int status;
	int field_octets;
	int octets;
	
	out[0] = HF_CMD;
	out[1] = CMD_READ_KEY_INFO;
	out[2] = key_num;
	
	if ( (key_num < 0) || (ut->info.keys_num <= key_num) 
		|| ((sizeof(ut->keys)/sizeof(ut->keys[0])) <= key_num) )
	{
		ERROR(ut, "key number is out of range for this device");
		return (ERR_CRD_UT10XX_WRONG_KEY);
	}
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	octets = status;

	ut->keys[key_num].info = in[1];
	ut->keys[key_num].ecc_params = CRD_UT10XX_get_ecc_params(
		256 * (int)in[2] + (int)in[3]);

	field_octets = ut->info.ecc_params->field_octets;
	
	if (octets != 25 + 2*field_octets)
	{
		ut->keys[key_num].info = 0;
		ERROR(ut, "host received wrong number of key octets");
		return(ERR_CRD_UT10XX_RECEIVE);
	}
	
	strncpy(ut->keys[key_num].name, (char *)in + 4, 21);
	ut->keys[key_num].name[22] = '\0';

	memset(ut->keys[key_num].pub_x, 0, sizeof(ut->keys[key_num].pub_x));
	memset(ut->keys[key_num].pub_y, 0, sizeof(ut->keys[key_num].pub_y));
	
	memcpy(ut->keys[key_num].pub_x, in + 25, field_octets);
	memcpy(ut->keys[key_num].pub_y, in + 25 + field_octets, field_octets);
	
	return (0);
}

int 
CRD_UT10XX_cmd_read_public_obj(CRD_UT10XX_t *ut, int obj_num, char *obj)
{
	unsigned char out[4];
	unsigned char in[1 + PUBLIC_OBJ_OCTETS];
	int status;
	
	out[0] = HF_CMD;
	out[1] = CMD_READ_PUBLIC_OBJ;
	out[2] = (obj_num >> 8) & 0xFF;
	out[3] = obj_num & 0xFF;
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	memcpy(obj, in + 1, status - 1);
	
	return (0);
}

int 
CRD_UT10XX_cmd_rbg_generate_bits(CRD_UT10XX_t *ut, char *buf, int octets)
{
	unsigned char out[3];
	unsigned char in[DF_MAX];
	int status;
	
	out[0] = HF_CMD;
	out[1] = CMD_RBG_GENERATE_BITS;
	
	while (octets > 120)
	{
		out[2] = 120;
		
		status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
		
		if (status < 0)
		{
			ERROR(ut, CRD_UT10XX_lib_error_desc(status));
			return (status);
		}
			
		memcpy(buf, in + 1, 120);
		buf += 120;
		octets -= 120;
	}
	
	out[2] = octets;
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
		
	memcpy(buf, in + 1, status - 1);
	
	return (0);
}

int 
CRD_UT10XX_cmd_dev_unlock(CRD_UT10XX_t *ut, const char *pin)
{
	unsigned char out[11];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_DEV_UNLOCK;
	out[2] = CRD_UT10XX_SO_PIN_ID;
	
	if (strlen(pin) > 8)
	{
		ERROR(ut, "PIN is too long (at most 8 characters)");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	strncpy((char *)out + 3, pin, 8);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return status;
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_dev_erase(CRD_UT10XX_t *ut, const char *pin)
{
	unsigned char out[11];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_DEV_ERASE;
	out[2] = CRD_UT10XX_SO_PIN_ID;
	
	if (strlen(pin) > 8)
	{
		ERROR(ut, "PIN is too long (at most 8 characters)");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	strncpy((char *)out + 3, pin, 8);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return status;
	}
	
	memset(ut->keys, 0, sizeof(ut->keys));
	
	return (0);
}

int 
CRD_UT10XX_cmd_change_so_pin(CRD_UT10XX_t *ut, const char *pin, const char *new_pin)
{
	unsigned char out[19];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_CHANGE_SO_PIN;
	out[2] = CRD_UT10XX_SO_PIN_ID;
	
	if ((strlen(pin) > 8) || (strlen(new_pin) > 8))
	{
		ERROR(ut, "PIN is too long (at most 8 characters)");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	strncpy((char *)out + 3, pin, 8);
	strncpy((char *)out + 11, new_pin, 8);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return status;
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_ecdsa_sign(CRD_UT10XX_t *ut, const char *pin, int key_num, 
	char *sign, int sign_octets, const char *digest, int digest_octets)
{
	unsigned char out[HF_MAX];
	unsigned char in[DF_MAX];
	int status;
	int octets;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_ECDSA_SIGN;
	out[2] = CRD_UT10XX_USER_PIN_ID;
	
	if (strlen(pin) > 8)
	{
		ERROR(ut, "PIN is too long (at most 8 characters)");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	if ( (key_num < 0) || (ut->info.keys_num <= key_num) || 
		((sizeof(ut->keys)/sizeof(ut->keys[0])) <= key_num) )
	{
		ERROR(ut, "key number is out of range for this device");
		return (ERR_CRD_UT10XX_WRONG_KEY);
	}
		
	strncpy((char *)out + 3, pin, 8);
	out[11] = key_num;
	
	digest_octets = digest_octets < 64 ? digest_octets : 64;
	memcpy(out + 12, digest, digest_octets);
	
	status = CRD_UT10XX_single_pass(ut, out, 12 + digest_octets, in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	octets = status - 1;
	
	if (sign_octets < octets)
	{
		ERROR(ut, "buffer for signature is too small");
		return (ERR_CRD_UT10XX_TOO_SMALL_BUF);
	}
	
	memcpy(sign, in + 1, octets);
	
	return (octets);
}

int 
CRD_UT10XX_cmd_change_user_pin(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	const char *new_pin)
{
	unsigned char out[19];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_CHANGE_USER_PIN;
	out[2] = pin_id;
	
	if ((strlen(pin) > 8) || (strlen(new_pin) > 8))
	{
		ERROR(ut, "PIN is too long (at most 8 characters)");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	strncpy((char *)out + 3, pin, 8);
	strncpy((char *)out + 11, new_pin, 8);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return status;
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_generate_key(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	const char *key_name, int key_num)
{
	unsigned char out[36];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_GENERATE_KEY;
	
	if ( (strlen(pin) > 8) || ((pin_id != CRD_UT10XX_SO_PIN_ID) 
		&& (pin_id != CRD_UT10XX_USER_PIN_ID)) || (strlen(key_name) > 21) )
	{
		ERROR(ut, "PIN is too long or its id is wrong or key name "
			"is too long");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	if ( (key_num < 0) || (ut->info.keys_num <= key_num) 
		|| ((sizeof(ut->keys)/sizeof(ut->keys[0])) <= key_num) )
	{
		ERROR(ut, "key number is out of range for this device");
		return (ERR_CRD_UT10XX_WRONG_KEY);
	}
	
	if (ut->info.ecc_params == 0)
	{
		ERROR(ut, "ecc_params field is not initialized");
		return (ERR_CRD_UT10XX_NOT_INIT);
	}
	
	out[2] = pin_id;
	strncpy((char *)out + 3, pin, 8);
	out[11] = key_num;
	out[12] = 0;
	out[13] = ((ut->info.ecc_params->id) >> 8) & 0xff;
	out[14] = ((ut->info.ecc_params->id) >> 0) & 0xff;
	strncpy((char *)out + 15, key_name, 21);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_delete_key(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	int key_num)
{
	unsigned char out[12];
	unsigned char in[2];
	int status;
	
	memset(out, 0, sizeof(out));
	
	out[0] = HF_CMD;
	out[1] = CMD_DELETE_KEY;
	
	if ( (strlen(pin) > 8) || ((pin_id != CRD_UT10XX_SO_PIN_ID) 
		&& (pin_id != CRD_UT10XX_USER_PIN_ID)) )
	{
		ERROR(ut, "PIN is too long or its id is wrong");
		return (ERR_CRD_UT10XX_ARG_RANGE);
	}
	
	if ( (key_num < 0) || (ut->info.keys_num <= key_num) 
		|| ((sizeof(ut->keys)/sizeof(ut->keys[0])) <= key_num) )
	{
		ERROR(ut, "key of this number is out of range for this device");
		return (ERR_CRD_UT10XX_WRONG_KEY);
	}
	
	out[2] = pin_id;
	strncpy((char *)out + 3, pin, 8);
	out[11] = key_num;
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_write_public_obj(CRD_UT10XX_t *ut, const char *pin, int pin_id, 
	int obj_num, const char *obj)
{
	unsigned char out[13 + PUBLIC_OBJ_OCTETS];
	unsigned char in[2];
	int status;
	
	out[0] = HF_CMD;
	out[1] = CMD_WRITE_PUBLIC_OBJ;
	out[2] = pin_id;
	strncpy((char *)out + 3, pin, 8);
	out[11] = (obj_num >> 8) & 0xFF;
	out[12] = obj_num & 0xFF;
	memcpy(out + 13, obj, PUBLIC_OBJ_OCTETS);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	return (0);
}

int 
CRD_UT10XX_cmd_try_pin(CRD_UT10XX_t *ut, const char *pin, int pin_id)
{
	unsigned char out[11];
	unsigned char in[2];
	int status;
	
	out[0] = HF_CMD;
	out[1] = CMD_TRY_PIN;
	out[2] = pin_id;
	strncpy((char *)out + 3, pin, 8);
	
	status = CRD_UT10XX_single_pass(ut, out, sizeof(out), in, sizeof(in));
	
	memset(out, 0, sizeof(out));
	
	if (status < 0)
	{
		ERROR(ut, CRD_UT10XX_lib_error_desc(status));
		return (status);
	}
	
	return (0);
}

int 
CRD_UT10XX_fs_get_file_list(CRD_UT10XX_t *ut)
{
	unsigned char obj[PUBLIC_OBJ_OCTETS];
	int status;
	int i;
	int j;
	
	for (i = 0; i < 8; i++)
		ut->free_sectors[i] = 0;
	
	for (i = 8; i < 128; i++)
		ut->free_sectors[i] = i;
	
	for (i = 0; i < ut->files_no; i++)
	{
		status = CRD_UT10XX_cmd_read_public_obj(ut, i, (char *)obj);
		
		if (status < 0)
		{
			ERROR(ut, "can not read file information table");
			return (status);
		}
		
		ut->file_types[i] = obj[0];
		
		if (ut->file_types[i] != CRD_UT10XX_FT_EMPTY)
		{
			ut->file_sizes[i] = 256 * (int)obj[2] + (int)obj[3];
			memcpy(ut->file_names_tb + 32*i, obj + 4, 28);
			ut->file_names[i] = ut->file_names_tb + 32*i;
			
			for (j = 32; j < 64; j++)
			{
				if (obj[j] == 0)
					break;
				
				ut->free_sectors[obj[j]] = 0;
			}
		}
	}
	
	return (0);
}

int
CRD_UT10XX_fs_clear(CRD_UT10XX_t *ut, const char *pin, int pin_id)
{
	unsigned char obj[PUBLIC_OBJ_OCTETS];
	int status;
	int i;
	
	memset(obj, 0, PUBLIC_OBJ_OCTETS);
	
	for (i = 0; i < 256; i++)
	{
		status = CRD_UT10XX_cmd_write_public_obj(ut, pin, pin_id, i, (char *)obj);
		
		if (status < 0)
		{
			ERROR(ut, "could not write to device public memory");
			return (status);
		}
	}
	
	return (0);
}

int
CRD_UT10XX_fs_scan(CRD_UT10XX_t *ut, char *str)
{
	unsigned char mem[16384];
	unsigned char *finfo;
	int status;
	int offset;
	int i;
	int j;
	
	for (i = 0, offset = 0; offset < sizeof(mem); i++, offset += PUBLIC_OBJ_OCTETS)
	{
		status = CRD_UT10XX_cmd_read_public_obj(ut, i, (char *)mem + offset);
		
		if (status < 0)
		{
			ERROR(ut, "could not read device public memory");
			return (status);
		}
	}
	
	for (i = 0, offset = 0; i < 16; i++, offset += PUBLIC_OBJ_OCTETS)
	{
		finfo = mem + offset;
		
		str += sprintf(str, "[%01X] FILE INFO\n", i);
		
		switch (finfo[0])
		{
			case CRD_UT10XX_FT_EMPTY:
				str += sprintf(str, "\ttype: EMPTY\n");
				break;
			case CRD_UT10XX_FT_CRT:
				str += sprintf(str, "\ttype: CRT\n");
				break;
			default:
				str += sprintf(str, "\ttype: UNKNOWN\n");
				break;
		}
		
		str += sprintf(str, "\tsize: %d\n", 256 * (int)finfo[2] + (int)finfo[3]);
		str += sprintf(str, "\tname: %s\n", finfo + 4);
		str += sprintf(str, "\tsectors:");
		
		for (j = 32; j < 64; j++)
		{
			if (finfo[j] == 0)
				break;
			
			str += sprintf(str, " %d", finfo[j]);
		}
		
		str += sprintf(str, "\n\t\n\t");
		
		for (j = 0; j < 64; j++)
		{
			str += sprintf(str, ":%02X", finfo[j]);
			
			if (j % 16 == 15)
				str += sprintf(str, "\n\t");
		}
		
		str += sprintf(str, "\n");
	}
	
	for (i = 8, offset = 16 * PUBLIC_OBJ_OCTETS; i < 128; i++, offset += 128)
	{
		str += sprintf(str, "[%02X] SECTOR\n\t", i);
		
		for (j = 0; j < 128; j++)
		{
			str += sprintf(str, ":%02X", mem[offset + j]);
			
			if (j % 16 == 15)
				str += sprintf(str, "\n\t");
		}
		
		str += sprintf(str, "\n");
	}
	
	return (0);
}

CRD_UT10XX_file_t* 
CRD_UT10XX_file_open(CRD_UT10XX_t *ut, const char *file_name)
{
	CRD_UT10XX_file_t *fd = 0;
	unsigned char obj[PUBLIC_OBJ_OCTETS];
	int status;
	int i;
	int j;
	
	for (i = 0; i < ut->files_no; i++)
	{
		if ( (ut->file_types[i] == CRD_UT10XX_FT_EMPTY)
			|| (strcmp(file_name, ut->file_names[i]) != 0) )
		{
			continue;
		}
		
		fd = malloc(sizeof(*fd));
		
		if (fd == 0)
		{
			ERROR(ut, "could not allocate memory");
			return (0);;
		}
		
		memset(fd, 0, sizeof(*fd));
		
		fd->ut = ut;
		fd->num = i;
		fd->type = ut->file_types[i];
		fd->size = ut->file_sizes[i];
		strcpy(fd->name, file_name);
		
		status = CRD_UT10XX_cmd_read_public_obj(ut, i, (char *)obj);
		
		if (status < 0)
		{
			ERROR(ut, "could not read file information table");
			free(fd);
			return (0);
		}
		
		for (j = 32; j < 64; j++)
		{
			if (obj[j] == 0)
				break;
			
			fd->sectors[j - 32] = obj[j];
		}
		
		for (j = 0; j < 32; j++)
		{
			if (fd->sectors[j] == 0)
				break;
			
			/* Read first part of file sector. */
			status = CRD_UT10XX_cmd_read_public_obj(ut, 2*fd->sectors[j], (char *)obj);
			
			if (status < 0)
			{
				ERROR(ut, "could not read file contents");
				free(fd);
				return (0);
			}
			
			memcpy(fd->contents + (2*j) * PUBLIC_OBJ_OCTETS, obj, PUBLIC_OBJ_OCTETS);
			
			/* Read second part of file sector. */
			status = CRD_UT10XX_cmd_read_public_obj(ut, 2*fd->sectors[j] + 1, (char *)obj);
			
			if (status < 0)
			{
				ERROR(ut, "could not read file contents");
				free(fd);
				return (0);
			}
			
			memcpy(fd->contents + (2*j + 1) * PUBLIC_OBJ_OCTETS, obj, 
				PUBLIC_OBJ_OCTETS);
		}
			
		fd->fpos = 0;
		fd->flushed = 1;
	}
	
	return (fd);
}

CRD_UT10XX_file_t* 
CRD_UT10XX_file_create(CRD_UT10XX_t *ut, const char *file_name, int type)
{
	CRD_UT10XX_file_t *fd = 0;
	int i;
	
	for (i = 0; i < ut->files_no; i++)
	{
		if ( (ut->file_types[i] != CRD_UT10XX_FT_EMPTY)
			&& (strcmp(file_name, ut->file_names[i]) == 0) )
		{
			ERROR(ut, "there exist file with that name");
			return (0);
		}
	}
	
	for (i = 0; i < ut->files_no; i++)
	{
		if (ut->file_types[i] != CRD_UT10XX_FT_EMPTY)
			continue;
		
		fd = malloc(sizeof(*fd));
		
		if (fd == 0)
		{
			ERROR(ut, "could not allocate memory");
			return (0);
		}
		
		memset(fd, 0, sizeof(*fd));
		
		fd->ut = ut;
		fd->num = i;
		fd->type = type;
		fd->size = 0;
		strcpy(fd->name, file_name);
		fd->fpos = 0;
		fd->flushed = 0;
#if 0
fprintf(stderr, "file created successfully\n");
fprintf(stderr, "\tfile num: %d\n", fd->num);
fprintf(stderr, "\tfile type: %02X\n", fd->type);
fprintf(stderr, "\tfile size: %d\n", fd->size);
fprintf(stderr, "\tfile name: %s\n\n", fd->name);
#endif
		break;
	}
	
	return (fd);
}

int 
CRD_UT10XX_file_type(CRD_UT10XX_file_t *fd)
{
	return (fd->type);
}

int
CRD_UT10XX_file_type_set_printable_mask(CRD_UT10XX_file_t *fd)
{
	int i;
	
	for (i = 0; i < fd->size; i++)
	{
		if (!isgraph(fd->contents[i]) && !isspace(fd->contents[i]))
		{
			fd->type &= ~CRD_UT10XX_FTM_PRINT;
			return (0);
		}
	}
	
	fd->type |= CRD_UT10XX_FTM_PRINT;
	
	return (1);
}

int 
CRD_UT10XX_file_rename(CRD_UT10XX_file_t *fd, const char *new_name, int type)
{
	if (28 < strlen(new_name))
	{
		ERROR(fd->ut, "new file name is too long");
		return (/*err*/-1);
	}
	
	fd->type = type;
	strcpy(fd->name, new_name);
	fd->flushed = 0;
	
	return (0);
}

int 
CRD_UT10XX_file_set_fpos(CRD_UT10XX_file_t *fd, int fpos)
{
	if (fpos == CRD_UT10XX_FPOS_END)
	{
		fd->fpos = fd->size;
	}
	else if (fpos == CRD_UT10XX_FPOS_BEGIN)
	{
		fd->fpos = 0;
	}
	else if (fpos == CRD_UT10XX_FPOS_CUR)
	{
		fd->fpos = fd->fpos;
	}
	else if (fpos < fd->size)
	{
		fd->fpos = fpos;
	}
	else
	{
		ERROR(fd->ut, "there is no such position in this file");
		return (/*err*/-1);
	}
	
	return (fd->fpos);
}

int 
CRD_UT10XX_file_read(CRD_UT10XX_file_t *fd, char *buf, int size)
{
	int end = fd->fpos + size;
	int nr = 0;
	
	if (size <= 0)
	{
		ERROR(fd->ut, "size have to be positive");
		return (/*err*/-1);
	}
	
	while ( (fd->fpos < fd->size) && (fd->fpos < end) )
	{
		*buf++ = fd->contents[fd->fpos++];
		nr += 1;
	}
	
	return (nr);
}

int 
CRD_UT10XX_file_write(CRD_UT10XX_file_t *fd, const char *buf, int size)
{
	int avaliable = 0;
	int need = 0;
	int i;
	int j;
	
	if (size <= 0)
	{
		ERROR(fd->ut, "size have to be positive number");
		return (/*err*/-1);
	}
	
	if (sizeof(fd->contents) < fd->fpos + size)
	{
		ERROR(fd->ut, "too much data to write");
		return (/*err*/-1);
	}
	
	if (fd->size < fd->fpos + size)
	{
		need = ((fd->fpos + size + 127) / 128) - ((fd->size + 127) / 128);
		
		for (i = 0; i < 128; i++)
		{
			if (fd->ut->free_sectors[i] != 0)
				avaliable += 1;
		}
		
		if (avaliable < need)
		{
			ERROR(fd->ut, "there is no enought free memory on device");
			return (/*err*/-1);
		}
		
		j = (fd->size + 127) / 128;
		
		for (i = 0; i < 128; i++)
		{
			if (need == 0)
				break;
			
			if (fd->ut->free_sectors[i] != 0)
			{
				fd->ut->free_sectors[i] = 0;
				fd->sectors[j++] = i;
				need -= 1;
			}
		}
		
		fd->size = fd->fpos + size;
	}
	
	memcpy(fd->contents + fd->fpos, buf, size);
	fd->fpos += size;
	fd->flushed = 0;
	
	return (size);
}

int 
CRD_UT10XX_file_flush(CRD_UT10XX_file_t *fd, const char *pin, int pin_id)
{
	unsigned char obj[PUBLIC_OBJ_OCTETS];
	int fnum = fd->num;
	int status;
	int i;
	
	/* There is no new data to flush. */
	if (fd->flushed == 1)
		return (0);

	/* Drop information about file to device. */
	memset(obj, 0, sizeof(obj));
	
	obj[0] = fd->type;
	obj[2] = (fd->size >> 8) & 0xFF;
	obj[3] = fd->size & 0xFF;
	strcpy((char *)obj + 4, fd->name);
	
	for (i = 0; i < 32; i++)
		obj[i + 32] = fd->sectors[i];

	status = CRD_UT10XX_cmd_write_public_obj(fd->ut, pin, pin_id, fnum, (char *)obj);
	
	if (status < 0)
	{
		ERROR(fd->ut, "could not write file information to device");
		return (status);
	}
	
	/* Drop information about file to fd->ut structure. */
	fd->ut->file_types[fnum] = fd->type;
	fd->ut->file_sizes[fnum] = fd->size;
	strcpy(fd->ut->file_names_tb + fnum*32, fd->name);
	fd->ut->file_names[fnum] = fd->ut->file_names_tb + fnum*32;
	
	/* Write contents of file to device. */
	for (i = 0; i < 32; i++)
	{
		if (fd->sectors[i] == 0)
			break;
		
		/* Write first part of sector to public memory. */
		status = CRD_UT10XX_cmd_write_public_obj(fd->ut, pin, pin_id, 
			2*fd->sectors[i], fd->contents + (2*i) * PUBLIC_OBJ_OCTETS);
		
		if (status < 0)
		{
			ERROR(fd->ut, "could not write file contents to device");
			return (status);
		}
		
		/* Write second part of sector to public memory. */
		status = CRD_UT10XX_cmd_write_public_obj(fd->ut, pin, pin_id, 
			2*fd->sectors[i] + 1, fd->contents + (2*i + 1) * PUBLIC_OBJ_OCTETS);
		
		if (status < 0)
		{
			ERROR(fd->ut, "could not write file contents to device");
			return (status);
		}
	}
	
	fd->flushed = 1;
	
	return (0);
}

int 
CRD_UT10XX_file_delete(CRD_UT10XX_file_t *fd, const char *pin, int pin_id)
{
	unsigned char obj[PUBLIC_OBJ_OCTETS];
	int fnum = fd->num;
	int status;
	int i;
	
	memset(obj, 0, sizeof(obj));
	
	status = CRD_UT10XX_cmd_write_public_obj(fd->ut, pin, pin_id, fnum, (char *)obj);
	
	if (status < 0)
	{
		ERROR(fd->ut, "");
		return (status);
	}
	
	for (i = 0; i < 32; i++)
	{
		if (fd->sectors[i] == 0)
			break;
		
		fd->ut->free_sectors[fd->sectors[i]] = fd->sectors[i];
	}
	
	fd->ut->file_types[fnum] = CRD_UT10XX_FT_EMPTY;
	fd->ut->file_sizes[fnum] = 0;
	memset(fd->ut->file_names_tb + 32*fnum, 0, 32);
	fd->ut->file_names[fnum] = 0;
	
	memset(fd, 0, sizeof(*fd));
	free(fd);
	
	return (0);
}

int 
CRD_UT10XX_file_close(CRD_UT10XX_file_t *fd)
{
	int nd;
	int i;
	
	if (fd)
	{
		/* Remove reserved sectors from unflushed file. */
		if (fd->flushed == 0)
		{
			/* Determine number of unflushed sectors. */
			nd = ((fd->size + 127) / 128) - ((fd->ut->file_sizes[fd->num] 
				+ 127) / 128);
			
			/* Determine index of first unreserved sector. */
			for (i = 0; i < 32; i++)
			{
				if (fd->sectors[i] == 0)
					break;
			}
			
			/* Add reserved but unflushed sectors to free_sectors table. */
			while (nd-- > 0)
			{
				i -= 1;
				fd->ut->free_sectors[fd->sectors[i]] = fd->sectors[i];
			}
		}
		
		/* Clear file descriptor and free it. */
		memset(fd, 0, sizeof(*fd));
		free(fd);
	}
	
	return (0);
}

/*
 *
 */

#ifndef NO_INCLUDE_LOCL
#include "error.h"
#include "cp210x.h"
#endif

#include <stdio.h>
#include <string.h>

#ifdef ERROR
#undef ERROR
#endif

#define ERROR(m) ERROR_push_msg(ut->err, m)

int 
CP210X_CRD_UT10XX_open(CRD_UT10XX_t *ut)
{
	int status = 0;
	int baud = CP210X_BAUD_RATE_GEN_FREQ / 250000;

	ut->handle = usb_open(ut->hw);
	
	if (ut->handle == 0)
	{
		ERROR("usb_open failed");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_UART, CP210X_UART_ENABLE, 0, 0, 0, CRD_UT10XX_TIME_OUT);
		
	if (status < 0)
	{
		ERROR("could not enable UART in CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_BAUDRATE, baud, 0, 0, 0, CRD_UT10XX_TIME_OUT);
	
	if (status < 0)
	{
		ERROR("could not set up baudrate in CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
		
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_BITS, CP210X_BITS_DATA_8, 0, 0, 0, CRD_UT10XX_TIME_OUT);

	if (status < 0)
	{
		ERROR("could not set up data bits for CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
		
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_CONTROL, CP210X_CONTROL_DTR | CP210X_CONTROL_RTS | 
		CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS, 0, 0, 0, 
		CRD_UT10XX_TIME_OUT);
		
	if (status < 0)
	{
		ERROR("could not set up control lines for CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	status = usb_set_configuration(ut->handle, 1);
	
	if (status < 0)
	{
		ERROR("could not set up cofiguration to 1");
		return (ERR_CRD_UT10XX_SET_UP);
	}
		
	status = usb_claim_interface(ut->handle, 0);
	
	if (status < 0)
	{
		ERROR("could not set up interface to 0");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	return (status);
}

int 
CP210X_CRD_UT10XX_close(CRD_UT10XX_t *ut)
{
	if (ut->handle)
	{
		usb_release_interface(ut->handle, 0);
#		ifndef WIN32
		usb_reset(ut->handle);
#		endif
		usb_close(ut->handle);
		ut->handle = 0;
	}
	
	return (0);
}

int 
CP210X_CRD_UT10XX_read(CRD_UT10XX_t *ut, unsigned char *buf, int octets, int timeout_ms)
{
	unsigned char in[DF_MAX];
	int total = 0;
	int status;
	
	while ( (status = usb_bulk_read(ut->handle, CP210X_IN_BULK_EP, (char *)in, 
		DF_MAX, timeout_ms)) == 0 );
	
	total += status;
	
	if (status <= 0)
	{
		ERROR("could not read bulk transfer from CP210X");
		return (ERR_CRD_UT10XX_RECEIVE);
	}
		
	while (total < in[0] + 1)
	{
		status = usb_bulk_read(ut->handle, CP210X_IN_BULK_EP, (char *)in + total, 
			DF_MAX - total, timeout_ms);
		
		total += status;
		
		if (status <= 0)
		{
			ERROR("could not read bulk transfer from CP210X");
			return (ERR_CRD_UT10XX_RECEIVE);
		}
	}
	
	if (octets < total - 1)
	{
		ERROR("could not store all data received from CP210X in given buffer");
		return (ERR_CRD_UT10XX_TOO_SMALL_BUF);
	}
	
	memcpy(buf, in + 1, total - 1);	
	
	return (total - 1);
}

int 
CP210X_CRD_UT10XX_write(CRD_UT10XX_t *ut, const unsigned char *buf, int octets)
{
	unsigned char out[HF_MAX];
	int status;
	
	if (HF_MAX - 1 < octets)
	{
		ERROR("given buffer is to large to transmit (larger than " 
			XSTR(HF_MAX) " octets)");
		return (ERR_CRD_UT10XX_TOO_LARGE_BUF);
	}
	
	out[0] = octets;	
	memcpy(out + 1, buf, octets);
	
	status = usb_bulk_write(ut->handle, CP210X_OUT_BULK_EP, (char *)out, octets + 1, 
		CRD_UT10XX_TIME_OUT);
	
	if (status != octets + 1)
	{
		ERROR("could not write bulk transfer to CP210X");
		return (ERR_CRD_UT10XX_TRANSMIT);
	}
	
	return (octets);
}

/*
 *
 */
 
#ifndef NO_INCLUDE_LOCL
#include "error.h"
#endif

#include <stdio.h>
#include <string.h>

int 
ERROR_count(const char *err)
{
	int n = 0;
	
	do
	{
		if (*err == '\n')
			n += 1;
	}
	while (*err++);
	
	return (n);
}

int 
ERROR_push(char *err, int size, const char *file, const char *func, 
	const char *line, const char *msg)
{
	int len = strlen(err);
	
	if ( size < len + 32 + strlen(file) + strlen(func) + strlen(line) + strlen(msg) )
	{
		if ( (4 < len) && (strcmp(err + len - 5, "\n...\n") != 0) )
			sprintf(err + len, "...\n");
		
		return (-1);
	}
	
	sprintf(err + len, "%s(%s):%s:%s\n", file, line, func, msg);
	
	return (0);
}

const char* 
ERROR_pop(char *err)
{
	char *head = err;
	
	do
	{
		if ( (*err == '\n') && (*(err + 1) != '\0') )
			head = ++err;
	}
	while (*err++);
	
	err--;
	
	if (head != err)
	{
		while (head != (--err))
			*err = *(err - 1);
		
		*head++ = '\0';
	}
	else
	{
		head = 0;
	}
	
	return (head);
}

/*
 *
 */
 
#ifndef NO_INCLUDE_LOCL
#include "sec.h"
#endif

const unsigned char SECP160R1_named[7] = 
{
	/* OBJECT IDENTIFIER secp160r1 (1 3 132 0 8) */
	0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x08
};

const unsigned char SECP160R1_specified[178] = 
{
	/* SEQUENCE */
	0x30, 0x81, 0xAF, 
		/* INTEGER version */
		0x02, 0x01, 
		0x01, 
		/* SEQUENCE fieldID */
		0x30, 0x20, 
			/* OBJECT IDENTIFIER prime-field (1 2 840 10045 1 1) */
			0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 
			0x01, 
			/* INTEGER p */
			0x02, 0x15, 
			0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 
		/* SEQUENCE curve */
		0x30, 0x43, 
			/* OCTET STRING a */
			0x04, 0x14, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0x7F, 0xFF, 0xFF, 0xFC, 
			/* OCTET STRING b */
			0x04, 0x14, 
			0x1C, 0x97, 0xBE, 0xFC, 0x54, 0xBD, 0x7A, 0x8B, 
			0x65, 0xAC, 0xF8, 0x9F, 0x81, 0xD4, 0xD4, 0xAD, 
			0xC5, 0x65, 0xFA, 0x45, 
			/* BIT STRING seed */
			0x03, 0x15, 0x00, 
			0x10, 0x53, 0xCD, 0xE4, 0x2C, 0x14, 0xD6, 0x96, 
			0xE6, 0x76, 0x87, 0x56, 0x15, 0x17, 0x53, 0x3B, 
			0xF3, 0xF8, 0x33, 0x45, 
		/* OCTET STRING base */
		0x04, 0x29, 
		0x04, 0x4A, 0x96, 0xB5, 0x68, 0x8E, 0xF5, 0x73, 
		0x28, 0x46, 0x64, 0x69, 0x89, 0x68, 0xC3, 0x8B, 
		0xB9, 0x13, 0xCB, 0xFC, 0x82, 0x23, 0xA6, 0x28, 
		0x55, 0x31, 0x68, 0x94, 0x7D, 0x59, 0xDC, 0xC9, 
		0x12, 0x04, 0x23, 0x51, 0x37, 0x7A, 0xC5, 0xFB, 
		0x32, 
		/* INTEGER order */
		0x02, 0x15, 
    		0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x01, 0xF4, 0xC8, 0xF9, 0x27, 0xAE, 
	        0xD3, 0xCA, 0x75, 0x22, 0x57, 
		/* INTEGER cofactor */
		0x02, 0x01, 
		0x01
};

const unsigned char SECP160R1_subject_public_key_info_alg[18] =
{
	/* SEQUENCE */
	0x30, 0x10,
		/* OBJECT IDENTIFIER id-ecPublicKey (1 2 840 10045 2 1) */
		0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 
		0x01,
		/* OBJECT IDENTIFIER secp160r1 (1 3 132 0 8) */
		0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x08
};

const unsigned char SECP192R1_named[10] = 
{
	/* OBJECT IDENTIFIER secp192r1 (1 2 840 10045 3 1 1) */
	0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 
	0x01, 0x01
};

const unsigned char SECP192R1_specified[202] = 
{
	/* SEQUENCE */
	0x30, 0x81, 0xC7, 
		/* INTEGER version */
		0x02, 0x01, 
		0x01,
		/* SEQUENCE fieldID */
		0x30, 0x24,
			/* OBJECT IDENTIFIER prime-field (1 2 840 10045 1 1) */
			0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 
			0x01,
			/* INTEGER p */
			0x02, 0x19, 
			0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 
		/* SEQUENCE curve */
		0x30, 0x4B, 
			/* OCTET STRING a */
			0x04, 0x18, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 
			/* OCTET STRING b */
			0x04, 0x18, 
			0x64, 0x21, 0x05, 0x19, 0xE5, 0x9C, 0x80, 0xE7, 
			0x0F, 0xA7, 0xE9, 0xAB, 0x72, 0x24, 0x30, 0x49, 
			0xFE, 0xB8, 0xDE, 0xEC, 0xC1, 0x46, 0xB9, 0xB1, 
			/* BIT STRING seed */
			0x03, 0x15, 0x00, 
			0x30, 0x45, 0xAE, 0x6F, 0xC8, 0x42, 0x2F, 0x64, 
			0xED, 0x57, 0x95, 0x28, 0xD3, 0x81, 0x20, 0xEA, 
			0xE1, 0x21, 0x96, 0xD5, 
		/* OCTET STRING base */
		0x04, 0x31, 
		0x04, 0x18, 0x8D, 0xA8, 0x0E, 0xB0, 0x30, 0x90, 
		0xF6, 0x7C, 0xBF, 0x20, 0xEB, 0x43, 0xA1, 0x88, 
		0x00, 0xF4, 0xFF, 0x0A, 0xFD, 0x82, 0xFF, 0x10, 
		0x12, 0x07, 0x19, 0x2B, 0x95, 0xFF, 0xC8, 0xDA, 
		0x78, 0x63, 0x10, 0x11, 0xED, 0x6B, 0x24, 0xCD, 
		0xD5, 0x73, 0xF9, 0x77, 0xA1, 0x1E, 0x79, 0x48,
		0x11,
		/* INTEGER order */
		0x02, 0x19, 
		0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xDE, 0xF8, 
		0x36, 0x14, 0x6B, 0xC9, 0xB1, 0xB4, 0xD2, 0x28, 
		0x31,
		/* INTEGER cofactor */
		0x02, 0x01, 
		0x01
};

const unsigned char SECP192R1_subject_public_key_info_alg[21] =
{
	/* SEQUENCE */
	0x30, 0x13,
		/* OBJECT IDENTIFIER id-ecPublicKey (1 2 840 10045 2 1) */
		0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 
		0x01,
		/* OBJECT IDENTIFIER secp192r1 (1 2 840 10045 3 1 1) */
		0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 
		0x01, 0x01
};

const unsigned char SECP224R1_named[7] = 
{
	/* OBJECT IDENTIFIER secp224r1 (1 3 132 0 33) */
	0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x21
};

const unsigned char SECP224R1_specified[226] = 
{
	/* SEQUENCE */
	0x30, 0x81, 0xDF, 
		/* INTEGER version */
		0x02, 0x01, 
		0x01, 
		/* SEQUENCE fieldID */
		0x30, 0x28, 
			/* OBJECT IDENTIFIER prime-field (1 2 840 10045 1 1) */
			0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 
			0x01, 
			/* INTEGER p */
			0x02, 0x1D, 
			0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
			0x00, 0x00, 0x00, 0x00, 0x01, 
		/* SEQUENCE curve */
		0x30, 0x53, 
			/* OCTET STRING a */
			0x04, 0x1C, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFE, 
			/* OCTET STRING b */
			0x04, 0x1C, 
			0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 
			0xF5, 0x41, 0x32, 0x56, 0x50, 0x44, 0xB0, 0xB7, 
			0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43, 
			0x23, 0x55, 0xFF, 0xB4, 
			/* BIT STRING seed */
			0x03, 0x15, 0x00, 
			0xBD, 0x71, 0x34, 0x47, 0x99, 0xD5, 0xC7, 0xFC, 
			0xDC, 0x45, 0xB5, 0x9F, 0xA3, 0xB9, 0xAB, 0x8F, 
			0x6A, 0x94, 0x8B, 0xC5, 
		/* OCTET STRING base */
		0x04, 0x39, 
		0x04, 0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 
		0x7F, 0x32, 0x13, 0x90, 0xB9, 0x4A, 0x03, 0xC1, 
		0xD3, 0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 
		0xD6, 0x11, 0x5C, 0x1D, 0x21, 0xBD, 0x37, 0x63, 
		0x88, 0xB5, 0xF7, 0x23, 0xFB, 0x4C, 0x22, 0xDF, 
		0xE6, 0xCD, 0x43, 0x75, 0xA0, 0x5A, 0x07, 0x47, 
		0x64, 0x44, 0xD5, 0x81, 0x99, 0x85, 0x00, 0x7E, 
		0x34, 
		/* INTEGER order */
		0x02, 0x1D, 
		0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x16, 
		0xA2, 0xE0, 0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 
		0x45, 0x5C, 0x5C, 0x2A, 0x3D, 
		/* INTEGER cofactor */
		0x02, 0x01, 
		0x01
};

const unsigned char SECP224R1_subject_public_key_info_alg[18] =
{
	/* SEQUENCE */
	0x30, 0x10,
		/* OBJECT IDENTIFIER id-ecPublicKey (1 2 840 10045 2 1) */
		0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 
		0x01,
		/* OBJECT IDENTIFIER secp224r1 (1 3 132 0 33) */
		0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x21
};

const unsigned char SECP256R1_named[10] = 
{
	/* OBJECT IDENTIFIER secp256r1 (1 2 840 10045 3 1 7) */
	0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 
	0x01, 0x07
};

const unsigned char SECP256R1_specified[250] = 
{
	/* SEQUENCE */
	0x30, 0x81, 0xF7, 
		/* INTEGER version */
		0x02, 0x01, 
		0x01, 
		/* SEQUENCE fieldID */
		0x30, 0x2C, 
			/* OBJECT IDENTIFIER prime-field (1 2 840 10045 1 1) */
			0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 
			0x01, 
			/* INTEGER p */
			0x02, 0x21, 
			0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
			0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 
		/* SEQUENCE curve */
		0x30, 0x5B, 
			/* OCTET STRING a */
			0x04, 0x20, 
			0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
			0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 
			/* OCTET STRING b */
			0x04, 0x20, 
			0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 
			0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 
			0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 
			0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B, 
			/* BIT STRING seed */
			0x03, 0x15, 0x04, 
			0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 
			0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D, 0x26, 0xB7, 
			0x81, 0x9F, 0x7E, 0x90, 
		/* OCTET STRING base */
		0x04, 0x41, 
		0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 
		0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 
		0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 
		0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 
		0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 
		0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 
		0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 
		0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 
		0xF5, 
		/* INTEGER order */
		0x02, 0x21, 
		0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 
		0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
		0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 
		0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 
		0x51, 
		/* INTEGER cofactor */
		0x02, 0x01, 
		0x01
};

const unsigned char SECP256R1_subject_public_key_info_alg[21] =
{
	/* SEQUENCE */
	0x30, 0x13,
		/* OBJECT IDENTIFIER id-ecPublicKey (1 2 840 10045 2 1) */
		0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 
		0x01,
		/* OBJECT IDENTIFIER secp256r1 (1 2 840 10045 3 1 7) */
		0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 
		0x01, 0x07
};

/*
 *
 */
 
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef NO_INCLUDE_LOCL
#include "error.h"
#include "crd_ut10xx.h"
#endif

#ifndef NO_OSSL_ENGINE_INTERFACE

#include "openssl/ui.h"
#include "openssl/bio.h"
#include "openssl/pem.h"
#include "openssl/evp.h"
#include "openssl/ecdsa.h"
#include "openssl/objects.h"
#include "openssl/engine.h"
#include "openssl/x509.h"

/*
 * Error file generated by command:
 *
 *   perl ../util/mkerr.pl -conf e_stoken.ec -nostatic -staticloader -write e_stoken.c
 *
 * running in openssl/engines directory.
 */
#include "e_stoken_err.c"

#define LOG_LIB_ERROR(__err) \
	{ \
		if (stoken_log != NULL) \
		{ \
			fprintf(stoken_log, "! %s >> %s:%s:%s:%s\n", \
				stoken_time(), __FILE__, __func__, XSTR(__LINE__), \
				CRD_UT10XX_lib_error_desc(__err)); \
			fflush(stoken_log); \
		} \
	}

#define LOG_DEV_ERROR(__ut, __err) \
	{ \
		if (stoken_log != NULL) \
		{ \
			fprintf(stoken_log, "! %s >> %s:%s:%s:%s\n", \
				stoken_time(), __FILE__, __func__, XSTR(__LINE__), \
				CRD_UT10XX_lib_error_desc(__err)); \
			fprintf(stoken_log, "!\n"); \
			while (ERROR_count((__ut)->err)) \
				fprintf(stoken_log, "! \t%s\n", ERROR_pop((__ut)->err)); \
			fprintf(stoken_log, "!\n"); \
			fflush(stoken_log); \
		} \
	}

#define LOG_ERROR(__msg) \
	{ \
		if (stoken_log != NULL) \
		{ \
			fprintf(stoken_log, "! %s >> %s:%s:%s:%s\n", \
				stoken_time(), __FILE__, __func__, XSTR(__LINE__), \
				__msg); \
			fflush(stoken_log); \
		} \
	}

#define LOG_MSG(__msg) \
	{ \
		if (stoken_log != NULL) \
		{ \
			fprintf(stoken_log, "  %s >> %s\n", stoken_time(), __msg); \
			fflush(stoken_log); \
		} \
	}

/* 
 * This is temprorary (till OpenSSL implement final version of 
 * ECDSA_METHOD interface.
 */
typedef struct ecdsa_method 
{
	const char *name;
	ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int dgst_len, 
		const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
	int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, 
		BIGNUM **r);
	int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, 
		const ECDSA_SIG *sig, EC_KEY *eckey);
#if 0
	int (*init)(EC_KEY *eckey);
	int (*finish)(EC_KEY *eckey);
#endif
	int flags;
	char *app_data;
}
ecdsa_method;

static const char *stoken_id = "stoken";
static const char *stoken_name = "CMM Sigma sToken CRD-UT10xx engine support";

static const char *stokensoft_id = "stokensoft";
static const char *stokensoft_name = "CMM Sigma software sToken engine support";

static FILE *stoken_log = 0;
static char stoken_token_name[256];
static char stoken_key_name[256];
static char stoken_user_pin[9];

static char stokensoft_user_pin[128];

/* The definitions for control commands specific to sToken CRD-UT10XX engine. */
#define STOKEN_CMD_SET_LOG_FILE ENGINE_CMD_BASE
#define STOKEN_CMD_SET_TOKEN_NAME (ENGINE_CMD_BASE + 1)
#define STOKEN_CMD_SET_KEY_NAME (ENGINE_CMD_BASE + 2)
#define STOKEN_CMD_SET_USER_PIN (ENGINE_CMD_BASE + 3)
static const ENGINE_CMD_DEFN stoken_cmd_defns[] =
{
	{
		STOKEN_CMD_SET_LOG_FILE,
		"log_file",
		"Set log file to write engine messages",
		ENGINE_CMD_FLAG_STRING
	},
	{
		STOKEN_CMD_SET_TOKEN_NAME,
		"token_name",
		"Set token name which will be used by default",
		ENGINE_CMD_FLAG_STRING
	},
	{
		STOKEN_CMD_SET_KEY_NAME,
		"key_name",
		"Set key name which will be used by default",
		ENGINE_CMD_FLAG_STRING
	},
	{
		STOKEN_CMD_SET_USER_PIN,
		"user_pin",
		"Set User PIN which will be used for authentication",
		ENGINE_CMD_FLAG_STRING
	},
	{
		0, 
		NULL, 
		NULL, 
		0
	}
};

/* The definitions for control commands specific to software sToken engine. */
#define STOKENSOFT_CMD_SET_LOG_FILE ENGINE_CMD_BASE
#define STOKENSOFT_CMD_SET_USER_PIN (ENGINE_CMD_BASE + 1)
static const ENGINE_CMD_DEFN stokensoft_cmd_defns[] = 
{
	{
		STOKENSOFT_CMD_SET_USER_PIN,
		"user_pin",
		"Set User PIN or password which will be used for authentication",
		ENGINE_CMD_FLAG_STRING
	},
	{
		0, 
		NULL, 
		NULL, 
		0
	}
};



static const char* stoken_time(void);
static int stoken_add_all_digests(void);


static ECDSA_SIG *stoken_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, 
	const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
static int stoken_ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, 
	BIGNUM **r);

static ECDSA_METHOD stoken_ecdsa_method = 
{
	.name = "ECDSA (sToken CRD-UT10xx)",
	.ecdsa_do_sign = stoken_ecdsa_do_sign,
	.ecdsa_sign_setup = stoken_ecdsa_sign_setup,
	.ecdsa_do_verify = NULL,
	.flags = 0,
	.app_data = NULL
};


static int stoken_init(ENGINE *e);
static int stoken_finish(ENGINE *e);
static int stoken_destroy(ENGINE *e);
static int stoken_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void));
static EVP_PKEY* stoken_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, 
	void *callback_data);
static EVP_PKEY* stoken_load_public_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, 
	void *callback_data);

static int stokensoft_init(ENGINE *e);
static int stokensoft_finish(ENGINE *e);
static int stokensoft_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void));
static EVP_PKEY* stokensoft_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, 
	void *callback_data);

static int 
stoken_bind(ENGINE *e, const char *id)
{
	memset(stoken_token_name, 0, sizeof(stoken_token_name));
	memset(stoken_key_name, 0, sizeof(stoken_key_name));
	memset(stoken_user_pin, 0, sizeof(stoken_user_pin));
	
	memset(stokensoft_user_pin, 0, sizeof(stokensoft_user_pin));
	
	stoken_ecdsa_method.ecdsa_do_verify = 
		ECDSA_OpenSSL()->ecdsa_do_verify;
	
	/* Bind sToken CDR-UT10xx hardware engine. */
	if (id && (strcmp(id, stoken_id) == 0))
	{
		if ( !ENGINE_set_id(e, stoken_id) 
			|| !ENGINE_set_name(e, stoken_name) 
			|| !ENGINE_set_ECDSA(e, &stoken_ecdsa_method)
			|| !ENGINE_set_init_function(e, stoken_init) 
			|| !ENGINE_set_finish_function(e, stoken_finish) 
			|| !ENGINE_set_destroy_function(e, stoken_destroy) 
			|| !ENGINE_set_ctrl_function(e, stoken_ctrl)
			|| !ENGINE_set_load_privkey_function(e, stoken_load_private_key)
			|| !ENGINE_set_load_pubkey_function(e, stoken_load_public_key)
			|| !ENGINE_set_cmd_defns(e, stoken_cmd_defns) )
		{
			STOKENerr(STOKEN_F_STOKEN_BIND, STOKEN_R_COULD_NOT_SET_ENGINE);
			return (0);
		}
	}
	
    	/* Bind software sToken engine. */
	if (id && (strcmp(id, stokensoft_id) == 0))
	{
		if ( !ENGINE_set_id(e, stokensoft_id) 
			|| !ENGINE_set_name(e, stokensoft_name) 
			|| !ENGINE_set_ECDSA(e, ECDSA_OpenSSL())
			|| !ENGINE_set_init_function(e, stokensoft_init) 
			|| !ENGINE_set_finish_function(e, stokensoft_finish) 
			|| !ENGINE_set_destroy_function(e, stoken_destroy) 
			|| !ENGINE_set_ctrl_function(e, stokensoft_ctrl)
			|| !ENGINE_set_load_privkey_function(e, stokensoft_load_private_key)
			|| !ENGINE_set_cmd_defns(e, stokensoft_cmd_defns) )
		{
			STOKENerr(STOKEN_F_STOKEN_BIND, STOKEN_R_COULD_NOT_SET_ENGINE);
			return (0);
		}
	}
	
	/* Load error strings. */
	ERR_load_STOKEN_strings();
	
	return (1);
}

CRD_EXPORT_SYMBOL IMPLEMENT_DYNAMIC_CHECK_FN();
CRD_EXPORT_SYMBOL IMPLEMENT_DYNAMIC_BIND_FN(stoken_bind);

void ENGINE_load_stoken(void)
{
	ENGINE *e = 0;
	
	/* Add ECDSA with SHA-2 implemntations. */
	stoken_add_all_digests();
	
	/* Add sToken CRD-UT10xx. */
	if ( ((e = ENGINE_new()) == 0) || (stoken_bind(e, stoken_id) == 0) )
	{
		STOKENerr(STOKEN_F_ENGINE_LOAD_STOKEN, STOKEN_R_COULD_NOT_BIND_STOKEN_CRD_UT10XX);
		return;
	}
	
	ENGINE_add(e);
	ENGINE_free(e);
	
	/* Add software sToken. */
	if ( ((e = ENGINE_new()) == 0) || (stoken_bind(e, stokensoft_id) == 0) )
	{
		STOKENerr(STOKEN_F_ENGINE_LOAD_STOKEN, STOKEN_R_COULD_NOT_BIND_STOKEN_SOFT);
		return;
	}
	
	ENGINE_add(e);
	ENGINE_free(e);
	
	/* Clear error stack. */
	ERR_clear_error();
}

static const char*
stoken_time(void)
{
	static char at[32];
	time_t t = time(NULL);
	struct tm *utc = localtime(&t);
	
	strftime(at, sizeof(at), "[%d-%m-%Y] %H:%M:%S", utc);
	
	return (at);
}

static ECDSA_SIG *
stoken_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, 
	const BIGNUM *rp, EC_KEY *eckey)
{
	unsigned char prv_tb[128];
	unsigned char *prv_ptr = prv_tb;
	
	unsigned char sig_tb[128];
	const unsigned char *sig_ptr = sig_tb;
	int sig_len = 0;
	
	char *dev_name = (char *)prv_tb + 8;;
	int key_num = -1;
	char *pin = (char *)prv_tb + 20;;
	
	CRD_UT10XX_t *ut = 0;
	
	ECDSA_SIG *sig = 0;
	
	int status = 0;
	
	if ( i2d_ECPrivateKey(eckey, &prv_ptr) == 0 )
	{
		LOG_ERROR("could not convert private key");
		return (0);
	}
	
	key_num = prv_tb[19];
	
	if ( (ut = CRD_UT10XX_find_by_name(dev_name)) == 0 )
	{
		LOG_ERROR("could not find device")
		return (0);
	}
	
	if ( (status = CRD_UT10XX_session_open(ut)) < 0 )
	{
		LOG_DEV_ERROR(ut, status);
		return (0);
	}
	
	if ( (status = sig_len = CRD_UT10XX_cmd_ecdsa_sign(ut, pin, key_num, (char *)sig_tb, 
		sizeof(sig_tb), (char *)dgst, dgst_len)) < 0)
	{
		memset(prv_tb, 0, sizeof(prv_tb));
		LOG_DEV_ERROR(ut, status);
		return (0);
	}
	
	memset(prv_tb, 0, sizeof(prv_tb));
	
	if ( (status = CRD_UT10XX_session_close(ut)) < 0 )
	{
		LOG_DEV_ERROR(ut, status);
		return (0);
	}
	
	if ( d2i_ECDSA_SIG(&sig, &sig_ptr, sig_len) == 0 )
	{
		LOG_ERROR("could not convert device signature to OpenSSL ECDSA_SIG form");
		return (0);
	}
	
	LOG_MSG("ECDSA signature with 'stoken_ecdsa_do_sign' finished successfully");
	
	return (sig);
}

static int
stoken_ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **r)
{
	return (1);
}

static int 
stoken_init(ENGINE *e)
{
	int status;
	
	if ( ((status = CRD_UT10XX_lib_init()) < 0)
		|| ((status = CRD_UT10XX_lib_find_devices()) < 0) 
		|| ((status = CRD_UT10XX_lib_actualize_devices()) < 0) )
	{
		LOG_LIB_ERROR(status);
		return (0);
	}
	
	LOG_MSG("engine initialization with 'stoken_init' finished successfully");
	
	return (1);
}

static int 
stoken_finish(ENGINE *e)
{
	int status;
	
	memset(stoken_token_name, 0, sizeof(stoken_token_name));
	memset(stoken_key_name, 0, sizeof(stoken_key_name));
	memset(stoken_user_pin, 0, sizeof(stoken_user_pin));
	
	if ( (status = CRD_UT10XX_lib_finish()) != 0 )
	{
		LOG_LIB_ERROR(status);
		return (0);
	}
	
	LOG_MSG("engine 'stoken' finished successfully");
	
	return (1);
}

static int
stoken_destroy(ENGINE *e)
{
	ERR_unload_STOKEN_strings();
	return (1);
}

static int
stoken_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void))
{
	int ret = 1;
	
	switch (cmd)
	{
		case STOKEN_CMD_SET_LOG_FILE:
			if (p == NULL || (stoken_log = fopen((const char *)p, "a+")) == NULL)
				ret = 0;
			break;
		
		case STOKEN_CMD_SET_TOKEN_NAME:
			if (p == NULL)
				ret = 0;
			else
				strncpy(stoken_token_name, (const char *)p, sizeof(stoken_token_name));
			
			break;
		
		case STOKEN_CMD_SET_KEY_NAME:
			if (p == NULL)
				ret = 0;
			else
				strncpy(stoken_key_name, (const char *)p, sizeof(stoken_key_name));
			
			break;
		
		case STOKEN_CMD_SET_USER_PIN:
			if (p == NULL)
				ret = 0;
			else
				strncpy(stoken_user_pin, (const char *)p, sizeof(stoken_user_pin));
			
			break;
		
		default:
			ret = 0;
			break;
	}
	
	return (ret);
}

static EVP_PKEY*
stoken_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data)
{
	unsigned char prv_tb[128];
	const unsigned char *prv_ptr = prv_tb;
	
	char dev_key_id[512];
	char *dev_name = 0;
	char *dev_serial = 0;
	char *key_name = 0;
	char *key_num_str = 0;
	
	CRD_UT10XX_t *ut = 0;
	const CRD_UT10XX_key_info_t *key = 0;
	const CRD_UT10XX_ecc_params_t *params = 0;
	int key_num = -1;
	int status = 0;
	
	EVP_PKEY *evp_key = 0;
	
	UI *ui = 0;
	
	
	memset(prv_tb, 0, sizeof(prv_tb));
	memset(dev_key_id, 0, sizeof(dev_key_id));
	
	if (key_id)
	{
		strncpy(dev_key_id, key_id, sizeof(dev_key_id) - 1);
	}
	else
	{
		strcpy(dev_key_id, stoken_token_name);
		dev_key_id[strlen(stoken_token_name)] = '/';
		strcpy(dev_key_id + strlen(stoken_token_name) + 1, stoken_key_name);
	}
	
	dev_name = dev_key_id;
	key_name = strchr(dev_key_id, '/');
	
	if (key_name)
	{
		*key_name++ = '\0';
	}
	else
	{
		LOG_ERROR("key name is not in the form <dev_name>/<key_name>.");
		return (0);
	}
	
	dev_serial = strrchr(dev_name, ':');
	
	if (dev_serial)
	{
		*dev_serial++ = '\0';
	}
	
	key_num_str = strrchr(key_name, ':');
	
	if (key_num_str)
	{
		*key_num_str++ = '\0';
	}
	
	if (10 < strlen(dev_name))
	{
		LOG_ERROR("device name is too long, it must have at most 10 characters");
		return (0);
	}
	
	if (22 < strlen(key_name))
	{
		LOG_ERROR("key name is too long, it must have at most 22 characters");
		return (0);
	}
	
	if ( ((status = CRD_UT10XX_lib_find_devices()) < 0)
		|| ((status = CRD_UT10XX_lib_actualize_devices()) < 0) )
	{
		LOG_LIB_ERROR(status);
		return (0);
	}
	
	if ( ((ut = CRD_UT10XX_find_by_serial_number(dev_serial)) == 0)
		|| ((ut = CRD_UT10XX_find_by_name(dev_name)) == 0) )
	{
		LOG_ERROR("there is no device with specified name");
		return (0);
	}
	
	if (strcmp(dev_name, ut->info.name) != 0)
	{
		LOG_ERROR("device serial number and name does not match");
		return (0);
	}
	
	if (key_num_str)
	{
		key_num = atoi(key_num_str);
	}
	else if ( (status = key_num = CRD_UT10XX_find_key_by_name(ut, key_name)) < 0 )
	{
		LOG_DEV_ERROR(ut, status);
		return (0);
	}
	
	if ((key_num < 0) || (ut->info.keys_num <= key_num))
	{
		LOG_ERROR("key indsex is out of range");
		return (0);
	}
	
	key = (ut->keys) + key_num;
	params = key->ecc_params;
	
	if (stoken_user_pin[0] != '\0')
	{
		/* If ui_method is NULL, then ENGINE will try stoken_user_pin table. */
		memcpy(prv_tb + 20, stoken_user_pin, 8);
	}
	else
	{
		/* Get User PIN, to access device sign functionality (use UI method). */
		ui = UI_new_method(ui_method);
		UI_add_input_string(ui, "User PIN:", 0, (char *)prv_tb + 20, 0, 8);
		UI_process(ui);
		UI_free(ui);
	}
	
	/* Check if PIN is correct. */
	if ( (status = CRD_UT10XX_session_open(ut)) < 0 )
	{
		LOG_LIB_ERROR(status);
		return (0);
	}
	
	if ( (status = CRD_UT10XX_cmd_try_pin(ut, (char *)prv_tb + 20, CRD_UT10XX_USER_PIN_ID)) < 0 )
	{
		LOG_DEV_ERROR(ut, status);
		CRD_UT10XX_session_close(ut);
		return (0);
	}
	
	CRD_UT10XX_session_close(ut);
	
	/*
	 * Create DER encoding of EC private key.
	 */
	
	/* ECPrivateKey SEQUENCE */
	prv_tb[0] = 0x30;
	prv_tb[1] = 36 + 2*params->field_octets + params->asn1_named[1];
	/* version INTEGER */
	prv_tb[2] = 0x02;
	prv_tb[3] = 0x01;
	prv_tb[4] = 0x01;
	/* privateKey OCTET STRING (0x01 | <device name> | <key num> | <user PIN>) */
	prv_tb[5] = 0x04;
	prv_tb[6] = 0x15;
	prv_tb[7] = 0x01;
	memcpy(prv_tb + 8, ut->info.name, 11);
	prv_tb[19] = key_num;
	/* parameters [0] ECDomainParameters */
	prv_tb[28] = 0xA0;
	prv_tb[29] = 2 + params->asn1_named[1];
	memcpy(prv_tb + 30, params->asn1_named, prv_tb[29]);
	/* publicKey [1] BIT STRING */
	prv_tb[30 + prv_tb[29]] = 0xA1;
	prv_tb[31 + prv_tb[29]] = 4 + 2*params->field_octets;
	prv_tb[32 + prv_tb[29]] = 0x03;
	prv_tb[33 + prv_tb[29]] = 2 + 2*params->field_octets;
	prv_tb[34 + prv_tb[29]] = 0x00;
	prv_tb[35 + prv_tb[29]] = 0x04;
	memcpy(prv_tb + 36 + prv_tb[29], key->pub_x, params->field_octets);
	memcpy(prv_tb + 36 + prv_tb[29] + params->field_octets, key->pub_y, 
		params->field_octets);
	
	/* Convert private key DER encoding to OpenSSL internal form. */
	if (!d2i_PrivateKey(EVP_PKEY_EC, &evp_key, &prv_ptr, 2 + prv_tb[1]))
	{
		LOG_ERROR("OpenSSL internal error");
		memset(prv_tb, 0, sizeof(prv_tb));
		return (0);
	}
	
	LOG_MSG("key loading with 'stoken_load_private_key' finished successfully");

	/* Clear buffers. */
	memset(prv_tb, 0, sizeof(prv_tb));
	
	return (evp_key);
}

static EVP_PKEY*
stoken_load_public_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data)
{
	unsigned char pub_tb[128];
	const unsigned char *pub_ptr = pub_tb;
	
	char dev_key_name[11 + 23];
	char *dev_name = stoken_token_name;
	char *key_name = stoken_key_name;
	
	CRD_UT10XX_t *ut = 0;
	const CRD_UT10XX_key_info_t *key = 0;
	const CRD_UT10XX_ecc_params_t *params = 0;
	int key_num = -1;
	int status = 0;
	
	EVP_PKEY *evp_key = 0;
	
	memset(pub_tb, 0, sizeof(pub_tb));
	memset(dev_key_name, 0, sizeof(dev_key_name));
	
	if (key_id)
	{
		strncpy(dev_key_name, key_id, sizeof(dev_key_name) - 1);
		dev_name = dev_key_name;
		key_name = strchr(dev_key_name, '/');
		
		if (*key_name) 
		{
			*key_name++ = '\0';
		}
		else 
		{
			LOG_ERROR("key name is not in the form <dev_name>/<key_name>, where "
				"<dev_name> has at most 10 characters and <key_name> has at "
				"most 22 characters");
			return (0);
		}
		
		if (10 < strlen(dev_name))
		{
			LOG_ERROR("device name is too long, it must have at most 10 characters");
			return (0);
		}
		
		if (22 < strlen(key_name))
		{
			LOG_ERROR("key name is too long, it must have at most 22 characters");
			return (0);
		}
	}
	
	CRD_UT10XX_lib_find_devices();
	CRD_UT10XX_lib_actualize_devices();
	
	if ( (ut = CRD_UT10XX_find_by_name(dev_name)) == 0 )
	{
		LOG_ERROR("there is no device with specified name");
		return (0);
	}
	
	if ( (status = key_num = CRD_UT10XX_find_key_by_name(ut, key_name)) < 0 )
	{
		LOG_DEV_ERROR(ut, status);
		return (0);
	}
	
	key = (ut->keys) + key_num;
	params = key->ecc_params;
	
	/*
	 * Create DER encoding of EC public key.
	 */
	
	/* SubjectPublicKeyInfo SEQUENCE */
	pub_tb[0] = 0x30;
	pub_tb[1] = 6 + params->asn1_named_alg[1] + 2*params->field_octets;
	/* algorithm AlgorithmIdentifier */
	memcpy(pub_tb + 2, params->asn1_named_alg, 2 + params->asn1_named_alg[1]);
	/* subjectPublicKey BIT STRING */
	pub_tb[4 + pub_tb[3]] = 0x03;
	pub_tb[5 + pub_tb[3]] = 2 + 2*params->field_octets;
	pub_tb[6 + pub_tb[3]] = 0x00;
	pub_tb[7 + pub_tb[3]] = 0x04;
	memcpy(pub_tb + 8 + pub_tb[3], key->pub_x, params->field_octets);
	memcpy(pub_tb + 8 + pub_tb[3] + params->field_octets, key->pub_y, 
		params->field_octets);
	
	/* Convert public key DER encoding to OpenSSL internal form. */
	if (!d2i_PUBKEY(&evp_key, &pub_ptr, 2 + pub_tb[1]))
	{
		LOG_ERROR("OpenSSL internal error");
		return (0);
	}
	
	LOG_MSG("key loading with 'stoken_load_public_key' finished successfully");

	return (evp_key);
}

static int 
stokensoft_init(ENGINE *e)
{
	return (1);
}

static int 
stokensoft_finish(ENGINE *e)
{
	memset(stokensoft_user_pin, 0, sizeof(stokensoft_user_pin));
	return (1);
}

static int 
stokensoft_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void))
{
	int ret = 1;
	
	switch (cmd)
	{
		case STOKENSOFT_CMD_SET_USER_PIN:
			if (p == NULL)
				ret = 0;
			else
				strncpy(stokensoft_user_pin, (const char *)p, sizeof(stokensoft_user_pin));
			
			break;
		
		default:
			ret = 0;
			break;
	}
	
	return (ret);
}

static EVP_PKEY*
stokensoft_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data)
{
	BIO *in = 0;
	EVP_PKEY *key = 0;
	
	if ((in = BIO_new_file(key_id, "r")) == 0) {
		STOKENerr(STOKEN_F_STOKENSOFT_LOAD_PRIVATE_KEY, STOKEN_R_COULD_NOT_READ_KEY_FILE);
		return 0;
	}
	
	key = PEM_read_bio_PrivateKey(in, 0, 0, stokensoft_user_pin);
	
	BIO_free(in);
	
	return key;
}

#ifndef OPENSSL_NO_ECDSA
static const char *SN_ecdsaWithSha1 = "ecdsa-with-sha1";
static const char *LN_ecdsaWithSha1 = "ecdsa-with-sha1";
static const char *OBJ_ecdsaWithSha1 = "1.2.840.10045.4.1";

static int 
init(EVP_MD_CTX *ctx)
{
	return SHA1_Init(ctx->md_data);
}

static int 
update(EVP_MD_CTX *ctx, const void *data, size_t count)
{
	return SHA1_Update(ctx->md_data, data, count);
}

static int 
final(EVP_MD_CTX *ctx, unsigned char *md)
{
	return SHA1_Final(md, ctx->md_data);
}

static EVP_MD ecdsaWithSha1_md = 
{
	NID_undef,
	NID_undef,
	SHA_DIGEST_LENGTH,
	0,
	init,
	update,
	final,
	NULL,
	NULL,
	EVP_PKEY_ECDSA_method,
	SHA_CBLOCK,
	sizeof(EVP_MD *) + sizeof(SHA_CTX),
};

#ifndef OPENSSL_NO_SHA256
static const char *SN_ecdsaWithSha224 = "ecdsa-with-sha224";
static const char *LN_ecdsaWithSha224 = "ecdsa-with-sha224";
static const char *OBJ_ecdsaWithSha224 = "1.2.840.10045.4.3.1";

static const char *SN_ecdsaWithSha256 = "ecdsa-with-sha256";
static const char *LN_ecdsaWithSha256 = "ecdsa-with-sha256";
static const char *OBJ_ecdsaWithSha256 = "1.2.840.10045.4.3.2";

static int 
init224(EVP_MD_CTX *ctx)
{
	return SHA224_Init(ctx->md_data);
}

static int 
init256(EVP_MD_CTX *ctx)
{
	return SHA256_Init(ctx->md_data);
}

static int 
update256(EVP_MD_CTX *ctx, const void *data, size_t count)
{
	return SHA256_Update(ctx->md_data, data, count);
}

static int 
final256(EVP_MD_CTX *ctx, unsigned char *md)
{
	return SHA256_Final(md,ctx->md_data);
}

static EVP_MD ecdsaWithSha224_md =
{
	NID_undef,
	NID_undef,
	SHA224_DIGEST_LENGTH,
	0,
	init224,
	update256,
	final256,
	NULL,
	NULL,
	EVP_PKEY_ECDSA_method,
	SHA256_CBLOCK,
	sizeof(EVP_MD *) + sizeof(SHA256_CTX),
};

static EVP_MD ecdsaWithSha256_md =
{
	NID_undef,
	NID_undef,
	SHA256_DIGEST_LENGTH,
	0,
	init256,
	update256,
	final256,
	NULL,
	NULL,
	EVP_PKEY_ECDSA_method,
	SHA256_CBLOCK,
	sizeof(EVP_MD *) + sizeof(SHA256_CTX),
};

#endif /* ifndef OPENSSL_NO_SHA256 */

#ifndef OPENSSL_NO_SHA512
static const char *SN_ecdsaWithSha384 = "ecdsa-with-sha384";
static const char *LN_ecdsaWithSha384 = "ecdsa-with-sha384";
static const char *OBJ_ecdsaWithSha384 = "1.2.840.10045.4.3.3";

static const char *SN_ecdsaWithSha512 = "ecdsa-with-sha512";
static const char *LN_ecdsaWithSha512 = "ecdsa-with-sha512";
static const char *OBJ_ecdsaWithSha512 = "1.2.840.10045.4.3.4";

static int 
init384(EVP_MD_CTX *ctx)
{
	return SHA384_Init(ctx->md_data);
}

static int 
init512(EVP_MD_CTX *ctx)
{
	return SHA512_Init(ctx->md_data);
}

static int 
update512(EVP_MD_CTX *ctx, const void *data, size_t count)
{
	return SHA512_Update(ctx->md_data, data, count);
}

static int 
final512(EVP_MD_CTX *ctx, unsigned char *md)
{
	return SHA512_Final(md, ctx->md_data);
}

static EVP_MD ecdsaWithSha384_md =
{
	NID_undef,
	NID_undef,
	SHA384_DIGEST_LENGTH,
	0,
	init384,
	update512,
	final512,
	NULL,
	NULL,
	EVP_PKEY_ECDSA_method,
	SHA512_CBLOCK,
	sizeof(EVP_MD *) + sizeof(SHA512_CTX),
};

static EVP_MD ecdsaWithSha512_md =
{
	NID_undef,
	NID_undef,
	SHA512_DIGEST_LENGTH,
	0,
	init512,
	update512,
	final512,
	NULL,
	NULL,
	EVP_PKEY_ECDSA_method,
	SHA512_CBLOCK,
	sizeof(EVP_MD *) + sizeof(SHA512_CTX),
};

#endif /* ifndef OPENSSL_NO_SHA512 */

#define STOKEN_add_digest(nid, name) \
	if ( (nid = OBJ_create(OBJ_##name, SN_##name, LN_##name)) != NID_undef ) \
	{ \
		name##_md.type = nid; \
		name##_md.pkey_type = nid; \
		EVP_add_digest(&name##_md); \
	}

static int
stoken_add_all_digests(void)
{
	int nid;
	
	STOKEN_add_digest(nid, ecdsaWithSha1);
	STOKEN_add_digest(nid, ecdsaWithSha224);
	STOKEN_add_digest(nid, ecdsaWithSha256);
	STOKEN_add_digest(nid, ecdsaWithSha384);
	STOKEN_add_digest(nid, ecdsaWithSha512);
	
	return (1);
}

#endif /* ifndef OPENSSL_NO_ECDSA */

#endif /* ifndef NO_OSSL_ENGINE_INTERFACE */
