
package com.cloudpos.jniinterface;

public class PINPadInterface {
    /* native interface **/
    static {
        String fileName = "jni_cloudpos_pinpad";
        JNILoad.jniLoad(fileName);
    }

    /**
     * ALGO_CHECK_VALUE type
     **/
    public static final int ALGO_CHECK_VALUE_DEFAULT = 0;
    public static final int ALGO_CHECK_VALUE_SE919 = 1;

    /**
     * dukpt key type
     **/
    public final static int KEY_TYPE_DUKPT = 0;

    /**
     * tdukpt key type
     **/
    public final static int KEY_TYPE_TDUKPT = 1;
    /**
     * master-session key type
     **/
    public final static int KEY_TYPE_MASTER = 2;
    /**
     * public key type
     **/
    public final static int KEY_TYPE_PUBLIC = 3;
    /**
     * fix key type
     **/
    public final static int KEY_TYPE_FIX = 4;
    /**
     * tdukpt 2009 key type
     **/
    public final static int KEY_TYPE_TDUKPT_2009 = 5;

    /**
     * master keyID
     **/
    public static final int[] MASTER_KEY_ID = new int[]{
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09
    };
    /**
     * user keyID
     **/
    public static final int[] USER_KEY_ID = new int[]{
            0x00, 0x01
    };

    /**
     * 3DES
     **/
    public static final int ALGORITH_3DES = 1;
    /**
     * DES
     **/
    public static final int ALGORITH_DES = 0;
    public static final int ALGORITH_3DES24 = 5;

    /**
     * AES
     */
    public static final int ALGORITH_AES = 3;
    public static final int ALGORITH_AES24 = 6;
    public static final int ALGORITH_AES32 = 7;
    /**
     * SM4
     **/
    public static final int ALGORITH_SM4 = 2;

    public final static int MAC_METHOD_X99 = 0;
    public final static int MAC_METHOD_ECB_FIRST = 1;
    public final static int MAC_METHOD_SE919 = 2;
    public final static int MAC_METHOD_ECB = 3;
    public final static int MAC_METHOD_CMAC = 4;

    /**
     * MODE EBC
     **/
    public static final int PINPAD_ENCRYPT_STRING_MODE_EBC = 0;
    /**
     * MODE CBC
     **/
    public static final int PINPAD_ENCRYPT_STRING_MODE_CBC = 1;
    /**
     * MODE CFB
     **/
    public static final int PINPAD_ENCRYPT_STRING_MODE_CFB = 2;
    /**
     * MODE OFB
     **/
    public static final int PINPAD_ENCRYPT_STRING_MODE_OFB = 3;

    /**
     * Open the PINPad device.
     *
     * @return value >= 0, success; value < 0, error code
     **/
    public synchronized native static int open();

    /**
     * Close the PINPad device.
     *
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int close();

    public native static boolean isOpened();

    /**
     * Show text in the specified line.
     *
     * @param nLineIndex:  Line No. to display, 0 or 1.
     * @param arryText:    Text to show, String.getBytes().
     * @param nTextLength: Text length.
     * @param nFlagSound:  Not used.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int showText(int nLineIndex, byte[] arryText, int nTextLength,
                                      int nFlagSound);

    /**
     * Select master key and user key before encryption operations.
     *
     * @param nKeyType:     1 : TDUKPT, 2 : MASTER-SESSION PAIR, 5 : TDUKPT_2009.
     * @param nMasterKeyID: Master key id, 0-9 when nKeyType is master-session; Master key id, 0,1,2when nKeyType is TDUKPT or TDUKPT_2009
     * @param nUserKeyID:   User key id, used when nKeyType is master-session.
     * @param nAlgorith:    0,Not used
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int selectKey(int nKeyType, int nMasterKeyID, int nUserKeyID, int nAlgorith);

    /**
     * Set the max or min length of PIN. When you call the calculate_pin_block api, the number you can input is no more than the max length.
     *
     * @param nLength: PIN length
     * @param nFlag:   Flag, 0--min length     1--max length
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int setPinLength(int nLength, int nFlag);

    /**
     * Encrypt string. The user key should be a data key whose id is 2 in general.
     *
     * @param arryPlainText:        Plain text data buffer.
     * @param nTextLength:          Length of plain text data buffer.
     * @param arryCipherTextBuffer: buffer for saving cipher text.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int encrypt(byte[] arryPlainText, int nTextLength, byte[] arryCipherTextBuffer);

    /**
     * Calculate the PIN block of the inputted PIN. You should call select_key at first. The user key should be a PIN key whose id is 0 in general.
     *
     * @param arryASCIICardNumber: Card number in ASCII format.
     * @param nCardNumberLength:   Length of card number.
     * @param arryPinBlockBuffer:  buffer for saving PIN block.
     * @param nTimeout_MS:         Timeout waiting for user input in milliseconds. If it is less than 0, then wait forever.
     * @param nFlagSound:          Not used.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int calculatePINBlock(byte[] arryASCIICardNumber, int nCardNumberLength, byte[] arryPinBlockBuffer, int nTimeout_MS, int nFlagSound);

    /**
     * Calculate the MAC. The user key should be a MAC key whose id is 1 in general.
     *
     * @param arryData:         data buffer.
     * @param nDataLength:      Length of data buffer.
     * @param arryMACOutBuffer: buffer for saving mac result.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int calculateMac(byte[] arryData, int nDataLength, int nMACFlag, byte[] arryMACOutBuffer);

    /**
     * Update the user key. You should check the cipher user key by yourself through the specified master key and check value obtained from the server or other.
     *
     * @param nMasterKeyID:            Master key id.
     * @param nUserKeyID:              User key id.
     * @param arryCipherNewUserKey:    New user key in cipher text.
     * @param nCipherNewUserKeyLength: Length of new user key.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateUserKey(int nMasterKeyID, int nUserKeyID, byte[] arryCipherNewUserKey, int nCipherNewUserKeyLength);

    /**
     * Update master key, the master key must be ciphered by transport key.
     *
     * @param nMasterKeyID:              Master key id.
     * @param arryCipherNewMasterKey:    Ciphered master key.
     * @param nCipherNewMasterKeyLength: Length of ciphered master key.
     * @param pCheckValue:               Check value.
     * @param nCheckValueLen:            Length of check value.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateCipherMasterKey(int nMasterKeyID, byte[] arryCipherNewMasterKey,
                                                   int nCipherNewMasterKeyLength, byte[] pCheckValue, int nCheckValueLen);

    /**
     * Update the master key with check value.the master key must be ciphered by old master key
     *
     * @param nMasterKeyID              Master key id.
     * @param cipherNewMasterKey        ciphered master key which cipher by old master key.
     * @param nCipherNewMasterKeyLength Length of new master key.
     * @param CheckValue                Check value of user key.
     * @param checkValueLength          Length of check value.
     * @param algoCheckValue            0-- ALGO_CHECK_VALUE_DEFAULT;1-- ALGO_CHECK_VALUE_SE919(only for aisino).
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateSpecifiedMasterKey(int nDecryptMasterKeyID,  int nMasterKeyID, byte[] cipherNewMasterKey,
                                                     int nCipherNewMasterKeyLength, byte[] CheckValue,
                                                     int checkValueLength,int algoCheckValue);


    /**
     * Update the user key with check value. You don’t need to check the cipher user key by yourself.
     *
     * @param nMasterKeyID:            Master key id.
     * @param nUserKeyID:              User key id.
     * @param arryCipherNewUserKey:    New user key in cipher text.
     * @param nCipherNewUserKeyLength: Length of new user key.
     * @param nUserKeyType:            Key type. 0--PIN key;1--MAC key;2—Data key.
     * @param checkValue:              Check value of user key.
     * @param checkValueLength:        Length of check value, 4 bytes in general.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateUserKeyWithCheck(int nMasterKeyID, int nUserKeyID,
                                                    byte[] arryCipherNewUserKey, int nCipherNewUserKeyLength, int nUserKeyType,
                                                    byte[] checkValue,
                                                    int checkValueLength);

    /**
     * Update plain master key.
     *
     * @param nMasterKeyID:  Master key id.
     * @param arrayOldKey:   Old plain master key.
     * @param nOldKeyLength: Length of old key.
     * @param arrayNewKey:   New plain master key.
     * @param nNewKeyLength: Length of new key.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateMasterKey(int nMasterKeyID, byte[] arrayOldKey,
                                             int nOldKeyLength, byte[] arrayNewKey, int nNewKeyLength);

    /**
     * Update the user key with check value. You can set the the algorithm of check.
     *
     * @param nMasterKeyID:            Master key id.
     * @param nUserKeyID:              User key id.
     * @param arryCipherNewUserKey:    New user key in cipher text.
     * @param nCipherNewUserKeyLength: Length of new user key.
     * @param nUserKeyType:            Key type. 0--PIN key;1--MAC key;2—Data key.
     * @param checkValue:              Check value of user key.
     * @param checkValueLength:        Length of check value, 4 bytes in general.
     * @param algoCheckValue:          0-- ALGO_CHECK_VALUE_DEFAULT;1-- ALGO_CHECK_VALUE_SE919(only for aisino).
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateUserKeyWithCheckE(int nMasterKeyID, int nUserKeyID,
                                                     byte[] arryCipherNewUserKey, int nCipherNewUserKeyLength, int nUserKeyType,
                                                     byte[] checkValue, int checkValueLength, int algoCheckValue);

    /**
     * Update the user key with check value. You can set the the algorithm of check.
     *
     * @param nMasterKeyID: Master key id.
     * @param nUserKeyID:   User key id.
     * @param pTr31Msg:     TR31 Message.
     * @param nTr31MsgLen:  Length of TR31 Message.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateUserKeyWithTR31Format(int nMasterKeyID, int nUserKeyID,
                                                         byte[] pTr31Msg, int nTr31MsgLen);

    /**
     * Update master key, the master key must be ciphered by transport key, and use the selected algorithm of check .
     *
     * @param nMasterKeyID:              Master key id.
     * @param arryCipherNewMasterKey:    Ciphered master key.
     * @param nCipherNewMasterKeyLength: Length of ciphered master key.
     * @param pCheckValue:               Check value.
     * @param nCheckValueLen:            Length of check value.
     * @param algoCheckValue:            0-- ALGO_CHECK_VALUE_DEFAULT;1-- ALGO_CHECK_VALUE_SE919(only for aisino).
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int updateCipherMasterKeyE(int nMasterKeyID,
                                                    byte[] arryCipherNewMasterKey,
                                                    int nCipherNewMasterKeyLength, byte[] pCheckValue, int nCheckValueLen,
                                                    int algoCheckValue);

    /**
     * get serial number
     *
     * @param arrySerialNo serial number buffer return value.
     * @return < 0: error code >= 0: success, length of serial number
     **/
    public native static int getSerialNo(byte[] arrySerialNo);

    /**
     * set the callback, the callback method is pinpadCallback
     *
     * @return >= 0: success
     * -1: has not find lib
     * -2: has not find pinpad_set_pinblock_callback in lib
     * -3: has not find PinpadCallback in Java code
     **/
    public native static int setPinblockCallback();

    /**
     * Set bypass pin.permit click Enter key in PINPAD, bypass the pin.
     *
     * @param flag: 1--bypass;0--can not bypass.
     * @return >= 0 : success; <0: error code.
     **/
    public native static int setAllowBypassPinFlag(int flag);


    /**
     * encrypt string using user key
     *
     * @param arrayPlainText:        Plain text
     * @param arrayCipherTextBuffer: buffer for saving cipher text, for dukpt encrypt, the buffer data structure is: cipher data + KSN + counter.
     * @param nMode:                 PINPAD_ENCRYPT_STRING_MODE_EBC  0
     *                               PINPAD_ENCRYPT_STRING_MODE_CBC    1
     *                               PINPAD_ENCRYPT_STRING_MODE_CFB    2
     *                               PINPAD_ENCRYPT_STRING_MODE_OFB    3
     * @param arrayIV:               Initial vector, only for CBC, CFB, OFB mode
     * @param nIVLen:                Length of IV, must be equal to block length according to the algorithm
     * @return >= 0: success; the value is the length of the cipher data length; <0: error code.
     **/
    public native static int encryptWithMode(byte[] arrayPlainText, byte[] arrayCipherTextBuffer, int nMode, byte[] arrayIV, int nIVLen);

    /**
     * Get pinpad serial number.
     *
     * @param buffer: data buffer for saving the serial number.
     * @return >= 0: success; <0: error code.
     **/
    public native static int getHwserialno(byte[] buffer);

    private static PinPadCallbackHandler callbackHandler;

    public static int setupCallbackHandler(PinPadCallbackHandler handler) {
        callbackHandler = handler;
        if (handler != null) {
            return setPinblockCallback();
        }
        return 0;
    }

    /**
     * Call back method, called by driver.
     *
     * @param data: callback data from the driver, data[0] is the count of *, data[1] is other parameters
     **/
    public static void pinpadCallback(byte[] data) {
        if (callbackHandler != null) {
            callbackHandler.processCallback(data);
        }
    }

    /**
     * permission：android.permission.CLOUDPOS_PIN_LOAD_KEY.
     *
     * @param dukptKeyId : 0, 1, 2
     * @param keyUsage   0 : SESSION_KEY_USAGE_pin,
     *                   1 : SESSION_KEY_USAGE_mac,
     *                   2 : SESSION_KEY_USAGE_data,
     * @param snkey      :8 byte
     * @param counter    :4 byte
     * @param key        :16byte
     **/
    public native static int importDukptKey(int dukptKeyId, int keyUsage, byte[] snKey, byte[] counter, byte[] key);

    public native static int getMacForSnk(byte[] p1, byte[] p2, byte[] p3);

    /*
     * select the pinblock format
     * @param[in] : unsigned int nPinblockFormat : 0~5, ISO0/ISO1/ISO2/ISO3/ISO4/SM4
     * return value : < 0 : error code
     *                >= 0 : success.
     * Attention : in HAL layer, we only support ISO0, ISO2, ISO4, SM4.
     *             Case 0 : ISO2 is only for the function of IC card offline pin, pinblock is saved in the hardware security module.
     *                      HAL layer can not get it because it is not encrypted with a key.
     *             Case 1 : If you inject key with a specified algorithm and this algorithm is mismatched with the pinblock format,
     *                      hardware security module will return an error code. If they are matched, you are lucky!
     *             Case 2 : If you inject key without a specified algorithm, hardware security will calculate pinblock according to your choice.
     *             Case 3 : If you did not select pinblock format, hardware security module will select pinblock format
     *                      according to the algorithm you specified in the api hal_pinpad_select_key
     *
     **/
    public native static int selectPinblockFormat(int nPinblockFormat);


    /**
     * calcualte check value using specified master key, only allowed when the TK is existence.
     *
     * @param nMKID             : master key ID;
     * @param nAlgo             : now only support the default format, must be specified with 0
     * @param pCheckValueBuf    : buffer for the final result
     * @param nCheckValueBufLen : length of buffer, must be greater than or equal to 4
     *                          return value : < 0 : error code
     *                          > 0 : length of check value
     *                          never return 0;
     **/
    public native static int getMKCheckValue(int nMKID, int nAlgo, byte[] pCheckValueBuf, int nCheckValueBufLen);

    /**
     * calculate check value using specified session key. make sure that you have updated the session key in this slot
     *
     * @param nMKID             : master key ID;
     * @param nSKID             : session key ID;
     * @param nAlgo             : now only support the default format, must be specified with 0
     * @param pCheckValueBuf    : buffer for the final result
     * @param nCheckValueBufLen : length of buffer, must be greater than or equal to 4
     * @param nCheckValueBufLen : length of buffer, must be greater than or equal to 4
     *                          return value : < 0 : error code > 0 : length of check value never return 0;
     **/
    public native static int getSKCheckValue(int nMKID, int nSKID, int nAlgo, byte[] pCheckValueBuf, int nCheckValueBufLen);

    /* calcualte check value using specified TK key
     * @param[in] : unsigned int nTKID : TK ID;
     * @param[in] : unsigned int nCheckValueType : now only support the default format, must be specified with 0
     * @param[out] : unsigned char* pCheckValueBuf : buffer for the final result
     * @param[in] : unsigned int nCheckValueBufLen : length of buffer, must be greater than or equal to 4
     * return value : < 0 : error code
     *                > 0 : length of check value
     *                never return 0;
     * **/
    public native static int getTKCheckValue(int nTKID, int nCheckValueType, byte[] pCheckValueBuf, int nCheckValueBufLen);

    /**
     * Verify the MAC
     *
     * @param pData       : data
     * @param nDataLen    : data length
     * @param nMacFlag    : 0: X99, 1 : ECB, 2 : SE919, 3 : Union pay ECB, 4 : CMAC
     * @param pMacData    : mac data
     * @param nMacDataLen : mac data length, >=4 && <=8
     * @param nDirection  : 0 : response key, 1 : request key, 2 : both
     **/
    public native static int verifyResponseMac(byte[] pData, int nDataLen, int nMacFlag, byte[] pMacData, int nMacDataLen, int nDirection);

    /**
     * Set PINPAD GUI configuration. When setting flag 1 and 2, the setting will keep forever.
     *
     * @param nFlag       1 to set asterisk, 2 to set title bar
     * @param pConfigData when flag is 1, 0x00: Left, 0x01: Center, 0x02: Right; when flag is 2, the title content.
     * @return value : < 0 : error code
     * >= 0 : success
     **/
    public native static int setGUIConfiguration(int nFlag, byte[] pConfigData, int nDataLen);

    /* get the status of MK with the specified ID  **/
    /*
     * @param       : nMKID : master key ID
     * return value : < 0 : error code
     *                0 : it does not exist
     *                1 : it exists
     * **/
    public native static int getMkStatus(int nMKID);

    /* get the status of SK with the specified MKID and SKID **/
    /*
     * @param: nMKID  : master key ID
     * return value  : < 0 : error code
     *                 0 : it does not exist
     *                 1 : it exists
     * **/
    public native static int getSkStatus(int nMKID, int nSKID);


    /*
     * get the status of DUKPT Key
     * @param       : int nDukptID : ID of dukpt
     * @param       : byte[] pKSNBuf : buffer for saving KSN of this dukpt key
     * return vlaue : < 0 : error code
     *                0 : it does not exist
     *                > 0 : KSN data length
     **/
    public native static int getDukptStatus(int nDukptID, byte[] pKSNBuf);

    /*
     * create the SK in cipher text
     * @param[in] : unsigned int nMasterKeyID : master key ID, [0-49]
     * @param[out]: unsigned char* pCipherSkBuf : buffer for SK
     * @param[in] : unsigned int nCipherSkBufLen : buffer length.
     *                                      length of buffer must be greater than the length of expected SK
     *                                      Please pay attention, for AES key with the length of 24, the cipher SK length is 32
     * @param[in] : unsigned int nExpectedSKLen : length of SK
     *                                      Algorithm TDEA : 8,16,24
     *                                      Algorithm SM4 : 16
     *                                      Algorithm AES : 16, 24, 32
     * @param[in] : unsigned int nAlgorithm : 0 : reserved
     *                                        1 : TDEA
     *                                        2 : SM4
     *                                        3 : AES
     * @param[in] : unsigned int nParityCheck : 0 : no parity check
     *                                          1 : odd
     *                                          2 : even
     * return value :
     *              >= 0 : data length in the buffer
     *              < 0 : error code
     *
     * **/
    public native static int createSK(int nMasterKeyID, byte[] pCipherSKBuf, int nSKLen, int nExpectedSkLen, int nAlgorithm, int nParityCheck);

    /**
     * Set PINPAD GUI configuration. Now support setting style of PINPAD, setting whether prompt sound when input pin key.
     * <br>
     * This setting will erase after POS terminal power off.
     *
     * @param key   as follows table.
     * @param value as follows table.
     *              <table>
     *              <tr><th>key</th><th>value</th><th>default</th>
     *              </tr>
     *              <tr><td>style</td><td>system</td><td>system</td>
     *              </tr>
     *              <tr><td>sound</td><td>true/false</td><td>false</td>
     *              </tr>
     *              <tr><td>displaybackcolor</td><td>RGB color</td><td>#6C8C4F</td>
     *              </tr>
     *              <tr><td>inputtextcolor</td><td>RGB color</td><td>#FFFFFF</td>
     *              </tr>
     *              <tr><td>disablebackgrounddarkening</td><td>true/false</td><td>false</td>
     *              </tr>
     *              </table>
     * @return vlaue : < 0 : error code
     * >= 0 : success
     **/
    public native static int setPINPadUiConfiguration(String key, String value);

    /* added by maobiao.yao, 2020/12/16
     * change the PIN of card holder
     * @param[in] : unsigned char* pCardNumber : card number
     * @param[in] : unsigned char* nCardNumberLen : length of card number
     * @param[out] : unsigned char* pPinblockBuf_Old : buffer of pinblock for old PIN
     * @param[in/out] : unsigned int* pPinblockBufLen_Old : [in] : length of buffer for old pinblock
     *                                                      [out] : data length in the buffer for old pinblock
     * @param[out] : unsigned char* pPinblockBuf_New : buffer of pinblock of new PIN
     * @param[in/out] : unsigned int* pPinblockBufLen_New : [in] : length of buffer for new pinblock
     *                                                      [out] : data length in the buffer for new pinblock
     * @param[in] : int nTimeout_MS : timeout waiting for user input in milliseconds.
     * 									if it is less than 0 or bigger than 180 seconds, using the max waiting time 180 seconds.
     *
     * **/
    public native static int changePin(byte[] cardNum, byte[] pinOld, byte[] pinNew, int[] lengthResult, int timeout);//

    /*
     ** create new pin
     * @param[in] : unsigned char* pASCIICardNumber : card number in ASCII format
     * @param[in] : int nCardNumberLength : length of card number
     * @param[out] : unsigned char* pPinBlockBuffer : buffer for saving pin block
     * @param[in] : int nPinBlockBufferLength : buffer length of pin block
     * @param[in] : int nTimeout_MS : timeout waiting for user input in milliseconds, if it is less than zero, then wait forever
     * param[in]   : int nFlagSound : 0 : no voice prompt, 1 : voice prompt
     * return value : < 0 : error code
     *                >= 0 : success, length of pin block
     **/
    public native static int createPin(byte[] pASCIICardNumber, byte[] pPinBlockBuffer, int nTimeout_MS, int nFlagSound);

    /*
     * added by maobiao.yao, 2022/11/16
     * update the TK using the MK
     * @param[in] : unsigned int nTKID : slot index, [0-49]
     * @param[in] : unsigned char* pTKCipher : TK, cipher text, encrypted by MK
     * @param[in] : unsigned int nTKCipherLen : length of pTKCipher
     * @param[in] : unsigned int nAlgoDecrypt : [1 - 3] : 1 : TDEA, 2 : SM4, 3 : AES
     * @param[in] : unsigned char* pKCV : check value of TK
     * @param[in] : unsigned int nKCVLen : length of pKCV
     * @param[in] : unsigned int nAlgoKCV : 0 : default, 1 : se919[not support]
     **/
    public native static int updateTransportKeyWithCheck(int transportKeyID, byte[] cipherTransportKey, int algoCipherTransportKey, byte[] checkValue, int algoCheckValue);


    /**
     * encrypt string using user key
     *
     * @param arrayPlainText:        Plain text
     * @param arrayCipherTextBuffer: buffer for saving cipher text, for dukpt encrypt, the buffer data structure is: cipher data + KSN + counter.
     * @param nMode:                 PINPAD_ENCRYPT_STRING_MODE_EBC  0
     *                               PINPAD_ENCRYPT_STRING_MODE_CBC    1
     *                               PINPAD_ENCRYPT_STRING_MODE_CFB    2
     *                               PINPAD_ENCRYPT_STRING_MODE_OFB    3
     * @param arrayIV:               Initial vector, only for CBC, CFB, OFB mode
     * @param nIVLen:                Length of IV, must be equal to block length according to the algorithm
     * @return >= 0: success; the value is the length of the cipher data length; <0: error code.
     **/
    public native static int encryptWithModeResponse(byte[] arrayPlainText, byte[] arrayCipherTextBuffer, int nMode, byte[] arrayIV, int nIVLen);

    /*
     * decrypt string using data key
     * @param[in] : unsigned char* pCipherText : cipher text
     * @param[in] : int nCipherTextLength : length of cipher text
     * @param[out] : unsigned char* pPlainTextBuffer : buffer for saving plain text
     * @param[in] : int nPlainTextBufferLength : length of plain text buffer
     * @param[in] : unsigned int nMode :
     *                  PINPAD_ENCRYPT_STRING_MODE_EBC  0
     *                  PINPAD_ENCRYPT_STRING_MODE_CBC  1
     *                  PINPAD_ENCRYPT_STRING_MODE_CFB  2
     *                  PINPAD_ENCRYPT_STRING_MODE_OFB  3
     * @param[in] : unsigned char* PIV : initial vector, only for CBC, CFB, OFB mode
     * @param[in] : unsigned int nIVLen : length of IV, must be equal to block length according to the algorithm
     * return value : < 0 : error code
     *                >= 0 : success, length of cipher text length
     **/
    public native static int decryptWithMode(byte[] arrayPlainText, byte[] arrayCipherTextBuffer, int nMode, byte[] arrayIV);

    /**
     * If you select the TDUKPT, calculate the MAC using reponse MAC key
     * @param pData
     * @param nMACFlag 0: X99, 1 : ECB, 2 : SE919, 3 : Union pay ECB
     * @param pMACOutBuffer MAC data buffer
     * @return < 0 : error code >= 0 : success
     **/
    public native static int calculateResponseMac(byte[] pData, int nMACFlag, byte[] pMACOutBuffer);

    /**
     * calculate pin block with mode
     * @param[in] : unsigned char* pASCIICardNumber : card number in ASCII format
     * @param[in] : int nCardNumberLength : length of card number
     * @param[out] : unsigned char* pPinBlockBuffer : buffer for saving pin block
     * @param[in] : int nPinBlockBufferLength : buffer length of pin block
     * @param[in] : int nTimeout_MS : timeout waiting for user input in milliseconds, if it is less than zero, then wait forever
     * @param[in] : int nFlagSound : 0 : no voice prompt, 1 : voice prompt
     * @param[in] : nMode : default is 0, no mode, MODE_ESP : 10
     * @param[in] : unsigned char* pIV : IV data, for mode 0, don't care it, for MODE_ISP, 8 bytes is needed
     * @param[in] : unsigned int nIVLen : IV data length, for mode 0, don't care it, for MODE_ISP, must be 8
     * return value : < 0 : error code * >= 0 : success, length of pin block
     **/
    public native static int calculatePINBlockWithMode(byte[] arryASCIICardNumber, int nCardNumberLength,
                                                       byte[] arryPinBlockBuffer, int nTimeout_MS, int nFlagSound,
                                                       int nMode, byte[] arrayIV, int nIVLen );

    /**
     * calculate the MAC using current user key
     * @param[in] : unsigned char* pData : data
     * @param[in] : int nDataLength : data length
     * @param[in] : int nMACFlag : 0: X99, 1 : ECB， 2 ： Se919, 3 : uecb, 4 : CMAC
     * @param[in] : unsigned int nKeyVar : 0 : mac request, 1 : mac response, 2 : both
     * @param[out] : unsigned char* pMACOut : MAC data buffer
     * @param[in] : int nMACOutBufferLength : length of MAC data buffer
     * return value : < 0 : error code
     *                >= 0 : success
     *
     **/
    public native static int calculateMacEx(byte[] arryData, int nDataLength, int nMACFlag, int nKeyVar, byte[] arryMACOutBuffer);

    /*
     * get the status of DUKPT Key
     * @param       : int nDukptID : ID of dukpt
     * @param       : byte[] pKSNBuf : buffer for saving KSN of this dukpt key
     * return vlaue : < 0 : error code
     *                0 : it does not exist
     *                > 0 : KSN data length
     **/
    public native static int getDukptAesStatus(int nDukptID, byte[] pKSNBuf);

    /**
     * Update the user key with check value. You can set the the algorithm of check.
     *
     * @param masterKeyID: Master key id.
     * @param userKeyID:   User key id.
     * @param aryX9DataBlock:     X9_143 Message.
     * @return value >= 0, success; value < 0, error code
     **/
    public native static int  updateUserKeyX9_143(int masterKeyID, int userKeyID, byte[] aryX9DataBlock);
    /**
     * update transfer key using TR31 format, the protection key is old transfer key
     * @param[in] : unsigned int nPrevTransferKeyID : the slot index of old transfer key
     * @param[in] : unsigned char* pTr31Msg : message for TR31 format
     * return : < 0 : error code * >= 0 : success
    **/
    public native static int updateTkByTkTr31(int nPrevTransferKeyID, byte[] pTr31Msg);
    /**
     * update master key using TR31 format, the protection key is transfer key
     * @param[in] : unsigned int nTransferKeyID : the slot index of transfer key
     * @param[in] : unsigned char* pTr31Msg : message for TR31 format
     * return : < 0 : error code * >= 0 : success
     **/
    public native static int updateMkByTkTr31(int nTransferKeyID, byte[] pTr31Msg);
    /** update the master key using TR31 format, the protection key is old master key
     * @param[in] : unsigned int nMasterKeyID : the slot index of master key
     * @param[in] : unsigned char* pTr31Msg : message for TR31 format
     * return : < 0 : error code * >= 0 : success
     **/
    public native static int updateMkByMkTr31(int nPrevMasterKeyID, byte[] pTr31Msg);

}
