/**
 *  This file is used for platform independent functions.
 *
 */

#include "process_cmd_pinpad.h"
#include "tci.h"
#include "tl_spay_tui_msg.h"
#include "tl_tui_bc_error_msg.h"
#include "Vendor_Interface.h"
#include "TuiScreenProperty.h"
#include "TuiPinpadScreen.h"
#include "TuiPinpadState.h"
#include "spay_pin_random_util_tl.h"
#include "TuiLayout.h"

#define INVALID_NUMBER_OF_SAME_DIGITS	3

uint32_t generateRandom(void);
uint32_t wrapPinWithResult(spayTuiMsg_t * respmsg, uint32_t auth_src);
uint32_t wrapRandom(spayTuiMsg_t * respmsg);
uint32_t wrapPinAndRandom(spayTuiMsg_t * respmsg);
uint32_t wrapPin(getPinRsp_t * rsp);
uint32_t validateSo(uint8_t * so, uint32_t so_len);
uint32_t unwrapPinSo(spayTuiMsg_t * sendmsg, spayTuiMsg_t * respmsg);
uint32_t setupPin(uint8_t * so_buf, uint32_t so_len, uint8_t * tmp_buf);
uint32_t validatePin(void);

preference_t g_preference;
uint8_t gl_pin_verify[PIN_SIZE];
uint32_t gl_pin_verify_len;
uint8_t gl_min_pin_len;
uint8_t gl_pin_old[PIN_SIZE];	// existing pin
uint32_t gl_pin_old_len;
uint8_t g_nonce[NONCE_BUFFER_SIZE];
uint32_t g_nonce_len;
uint8_t g_ta_name[APP_NAME_BUFFER_SIZE];
uint32_t g_ta_name_len;
bool g_wallet_bio_auth = false; // used for send money in Unregistered Recipient case
bool g_is_pressed;

extern uint8_t g_pin[PIN_SIZE];
extern uint32_t g_pin_length;
extern loadable_img_t g_loadable_img[];
extern uint8_t g_close_tui_after_verify;
extern bool g_display_normal_pinbox;

void initPinpadAndData() {
	initPinpad();
	clearPinData();
}

/*
 *  This is what happens after use click "OK" or all PIN numbers are entered
 *  Return:
 */
uint32_t processPinpadState() {
	uint32_t ret = 0;
	switch (getPinpadState()) {
	case SPAY_TUI_ST_VERIFY_ENTER:
		// verify PIN
		if (gl_pin_verify_len != g_pin_length || g_pin_length == 0) {
			DBG_LOG("%s PIN length mismatch", LOG_TAG);
			setPinpadState(SPAY_TUI_ST_VERIFY_UNMATCH);
			g_display_normal_pinbox = false;
		} else {
			// replace "memcmp(g_pin, gl_pin_verify, g_pin_length) != 0" to prevent timing attack
			bool match = true;
			for (int i = 0; i < g_pin_length; i++)
				match &= (g_pin[i] == gl_pin_verify[i]);

			if (match) {
				////TTY_LOG("PIN Verify success!");
				setPinpadState(SPAY_TUI_ST_VERIFY_MATCH);
				setPinpadTimer();
			}
			else {
				DBG_LOG("%s PIN verify failed", LOG_TAG);
				setPinpadState(SPAY_TUI_ST_VERIFY_UNMATCH);
				g_display_normal_pinbox = false;
			}
		}
		break;
	case SPAY_TUI_ST_SETUP_ENTER_FIRST:
		// first entry for setup pin
		ret = validatePin();
		if (ret) {
			STORE_TA_ERROR("%s Invalid PIN entered", LOG_TAG);
			setPinpadState(SPAY_TUI_ST_SETUP_ENTER_INVALID);
		} else {
			gl_pin_verify_len = g_pin_length;
			memcpy(gl_pin_verify, g_pin, g_pin_length);
		}
		break;

	case SPAY_TUI_ST_SETUP_ENTER_SECOND:
		// second entry for setup pin
//              //TTY_LOG("%s len=%d, vlen=%d, %d %d %d %d | %d %d %d %d", LOG_TAG, g_pin_length, gl_pin_verify_len,
//                      g_pin[0], g_pin[1], g_pin[2], g_pin[3], gl_pin_verify[0], gl_pin_verify[1], gl_pin_verify[2], gl_pin_verify[3]);

		if (gl_pin_verify_len != g_pin_length
			|| memcmp(g_pin, gl_pin_verify, g_pin_length)) {
			setPinpadState(SPAY_TUI_ST_SETUP_UNMATCH);
		} else {
			setPinpadState(SPAY_TUI_ST_SETUP_MATCH);
		}
		break;

	default:
		DBG_LOG("%s Error: current state is %u", LOG_TAG, getPinpadState());
		// should not come here
		ret = SPAY_TPP_ERROR_INVALID_STATE;
	}
	removeAllPin();
	clearPinData();

	return ret;
}


uint32_t process_cmd_pinpad(uint32_t cmd_id, tciMessage_t * sendmsg, uint32_t sendmsg_len, tciMessage_t * respmsg, uint32_t respmsg_len) {
	return processCmdPinpad(cmd_id, sendmsg, sendmsg_len, respmsg, respmsg_len);
}

uint32_t process_cmd_internal(
	uint32_t cmd_id,
	spayTuiMsg_t * sendmsg,
	uint32_t sendmsg_len,
	spayTuiMsg_t * respmsg,
	uint32_t respmsg_len
)
{
	uint32_t ret = TIMA_SUCCESS;

	uint32_t img_len, press_len, unpress_len, normal_pin_buf_len,
	    error_pin_buf_len;

	uint8_t tmp_buf[SO_BUFFER_SIZE] = { 0 };
	uint32_t screenWidth = getScreenWidth();
	uint32_t screenHeight = getScreenHeight();
	uint32_t softkeyHeight = PINPAD_SOFTKEY_HEIGHT;
#if defined(SUPPORT_DISPLAY_HIDE_NOTCH)	
	uint32_t bottomBarHeight = PINPAD_BOTTOM_BAR_HEIGHT;
#endif	

	IS_BUFFER_VALID(sendmsg_len, spayTuiCommand_t, respmsg_len,
			spayTuiRsp_t) {
		TTY_LOG("%s Command and Response buffers validated", LOG_TAG);
	}
	else {
		STORE_TA_ERROR
		    ("Invalid command and response buffer lengths: sendmsg_len=%u size(spayTuiCommand_t)=%d, respmsg_len=%u, size(spayTuiRsp_t)=%d",
		     sendmsg_len, sizeof(spayTuiCommand_t), respmsg_len,
		     sizeof(spayTuiRsp_t));
		return TIMA_ERROR_INVALID_ARGUMENT;
	}

	switch (cmd_id) {
#ifdef USE_MOBICORE
	case SPAY_TUI_CMD_DRV_PIN:
		// (MC only!) TUI driver event

		if ((getPinpadState() & _SPAY_TUI_ST_START) == 0) {
			DBG_LOG("%s Touch event when TUI is not in 'start' states, only check if 'cancel' event", LOG_TAG);
			setPinpadState(processTuiEvents(true));
			ret = TIMA_ERROR_TUI_NO_SESSION;
			break;
		}

		DBG_LOG("%s Process Touch event, st = 0x%x, in_start_sates? = 0x%x",
		     LOG_TAG, getPinpadState(), (getPinpadState() & _SPAY_TUI_ST_START));
		setPinpadState(processTuiEvents(false));

		if (getPinpadState() & _SPAY_TUI_ST_ENTER) {
			ret = processPinpadState();
		}

		break;
#endif

	case BC_TUI_CMD_SET_NAVIGATION_GESTURE:
		TTY_LOG("BC_TUI_CMD_SET_NAVIGATION_GESTURE : %d", sendmsg->payload.cmd.data.navigation.isGesture);
		ret = setNavigationGesture(sendmsg->payload.cmd.data.navigation.isGesture);
		if (ret != TUI_SUCCESS) {
			TTY_LOG("Failed to set navigation gesture");
		}
		break;

	case BC_TUI_CMD_SET_PIN_TOUCHABLE_VIEW:
		setPinResourceState(PIN_TUI_ST_TOUCHABLE_VIEW);

		TTY_LOG("BC_TUI_CMD_SET_PIN_TOUCHABLE_VIEW : %u", sendmsg->payload.cmd.data.screenView.viewId);
		DBG_LOG("View : %u, %u, %u, %u", sendmsg->payload.cmd.data.screenView.startX, sendmsg->payload.cmd.data.screenView.startY,
				sendmsg->payload.cmd.data.screenView.viewWidth, sendmsg->payload.cmd.data.screenView.viewHeight);
		DBG_LOG("Resource : %u, %u, %u", sendmsg->payload.cmd.data.screenView.eventSize, sendmsg->payload.cmd.data.screenView.originSize, sendmsg->payload.cmd.data.screenView.extraSize);

		setPinScreenView(sendmsg->payload.cmd.data.screenView.viewId, sendmsg->payload.cmd.data.screenView.startX, sendmsg->payload.cmd.data.screenView.startY,
					  sendmsg->payload.cmd.data.screenView.viewWidth, sendmsg->payload.cmd.data.screenView.viewHeight,
					  sendmsg->payload.cmd.data.screenView.eventSize, sendmsg->payload.cmd.data.screenView.originSize, sendmsg->payload.cmd.data.screenView.extraSize);
		break;

    case BC_TUI_CMD_SET_PIN_SCREEN_BACKGROUND:
        TTY_LOG("BC_TUI_CMD_SET_PIN_SCREEN_BACKGROUND : %u %u %u", sendmsg->payload.cmd.data.screenBackground.imageWidth, sendmsg->payload.cmd.data.screenBackground.imageHeight, sendmsg->payload.cmd.data.screenBackground.imageSize);

		setPinResourceState(PIN_TUI_ST_BACKGROUND);
        ret = setPinBackground(sendmsg->payload.cmd.data.screenBackground.imageSize, sendmsg->payload.cmd.data.screenBackground.imageWidth, sendmsg->payload.cmd.data.screenBackground.imageHeight);
        if (ret != TUI_SUCCESS) {
            TTY_LOG("Failed to set pin background");
        }
        break;

    case BC_TUI_CMD_SEND_RESOURCE_DATA_ARRAY:
        TTY_LOG("BC_TUI_CMD_SEND_RESOURCE_DATA_ARRAY");
        setPinpadImages(sendmsg->payload.cmd.data.screenResourceArray.type, sendmsg->payload.cmd.data.screenResourceArray.buf);
        break;

    case BC_TUI_CMD_SEND_PIN_RESOURCE_DATA:
        TTY_LOG("BC_TUI_CMD_SEND_PIN_RESOURCE_DATA : %u, %u", sendmsg->payload.cmd.data.screenResource.index, sendmsg->payload.cmd.data.screenResource.resource.len);
        if(getPinResourceState() == PIN_TUI_ST_BACKGROUND) {
			if (isExsitPinBackground()) {
				savePinBackgroundResource(sendmsg->payload.cmd.data.screenResource.index,
										  sendmsg->payload.cmd.data.screenResource.resource.buf,
										  sendmsg->payload.cmd.data.screenResource.resource.len);
			} else {
				TTY_LOG("Failed to save pin background resource");
			}
		}
        else if (getPinResourceState() == PIN_TUI_ST_TOUCHABLE_VIEW) {
			if(isExsitPinScreenView()) {
				savePinScreenViewResource(sendmsg->payload.cmd.data.screenResource.resource.buf,
									   sendmsg->payload.cmd.data.screenResource.resource.len);
			}
        }
        break;

	case SPAY_TUI_CMD_LOAD_PIN:	// load PIN secure object
		respmsg->spayhdr.header.len = sizeof(loadPinRsp_t);
		if (getPinpadState() != SPAY_TUI_ST_NONE) {
			STORE_TA_ERROR("%s Cannot load PIN, wrong state: 0x%x!\n", LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		ret = unwrapPinSo(sendmsg, respmsg);

		if (ret == 0 || ret == SPAY_TPP_TUI_UPGRADE_PIN_SO_VERSION) {
			respmsg->payload.rsp.data.loadPinRsp.existing_pin_len = gl_pin_old_len;
		}

		break;

	case SPAY_TUI_CMD_GET_PIN:	// get PIN secure object
		respmsg->spayhdr.header.len = sizeof(getPinRsp_t);

		if (getPinpadState() != SPAY_TUI_ST_SETUP_MATCH) {
			STORE_TA_ERROR("%s Cannot get PIN, wrong state: 0x%x != 0x%x",
				LOG_TAG, getPinpadState(), SPAY_TUI_ST_SETUP_MATCH);
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}
		DBG_LOG("%s wrap pin", LOG_TAG);

		ret = wrapPin(&respmsg->payload.rsp.data.getPinRsp);

		if (ret == 0) {
			respmsg->payload.rsp.data.getPinRsp.existing_pin_len = gl_pin_old_len;
		}
		//clean pin
		DBG_LOG("%s CHANGING STATE TO SPAY_TUI_ST_NONE", LOG_TAG);
		setPinpadState(SPAY_TUI_ST_NONE);
		break;

	case SPAY_TUI_CMD_GENERATE_RANDOM:{
			uint8_t pin_random[SPAY_RANDOM_NUMBER_SIZE] = { 0 };
			uint32_t pin_random_len = sizeof(pin_random);
			respmsg->spayhdr.header.len = sizeof(getPinRsp_t);

			if (getPinpadState() != SPAY_TUI_ST_NONE) {
				STORE_TA_ERROR("%s Cannot generate random, wrong state: 0x%x",
				     LOG_TAG, getPinpadState());
				ret = SPAY_TPP_ERROR_INVALID_STATE;
				break;
			}
			//TTY_LOG("%s wrap pin", LOG_TAG);
			ret = generateRandom();
			if (ret) {
				STORE_TA_ERROR("%s Error generating PIN random", LOG_TAG);
				break;
			} else {
				TTY_LOG("%s : PIN random generated successfully", LOG_TAG);
				if (!get_pin_random
				    (pin_random, &pin_random_len)) {
					STORE_TA_ERROR ("%s Error retrieving the pin random", LOG_TAG);
					return TIMA_ERROR_TUI_WRAP_ERROR;
				}

				DBG_DUMP(pin_random, pin_random_len);
			}
			gl_pin_verify_len = 0;	// make sure no PIN

			gl_pin_old_len = 0;	// make sure a new random will be generated

			ret = wrapPin(&respmsg->payload.rsp.data.getPinRsp);

			if (ret) {
				STORE_TA_ERROR("%s : wrap_pin error when generate random", LOG_TAG);
			}

			respmsg->payload.rsp.data.getPinRsp.existing_pin_len = gl_pin_old_len;

			break;
		}
	case SPAY_TUI_CMD_SET_PIN_BOX:	// set pin box properties, such as images, location and number of pin boxes
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		if (getPinpadState() != SPAY_TUI_ST_NONE &&
			getPinpadState() != SPAY_TUI_ST_VERIFY_MATCH) {
			STORE_TA_ERROR("%s Cannot set PIN box image, wrong state: 0x%x!",
			     LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}
		// how many PIN boxes
		if (sendmsg->payload.cmd.data.setPinBox.number > 0)
			gl_min_pin_len = sendmsg->payload.cmd.data.setPinBox.number;

		if (gl_min_pin_len < US_PIN_SIZE || gl_min_pin_len > PIN_SIZE) {
			STORE_TA_ERROR("%s invalid PIN box number: %d", LOG_TAG,
				gl_min_pin_len);
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			gl_min_pin_len = US_PIN_SIZE;
			break;
		}

		img_len = sendmsg->payload.cmd.data.setPinBox.images.len;
		normal_pin_buf_len =
		    sendmsg->payload.cmd.data.setPinBox.normal_pin_buf_len;
		error_pin_buf_len =
		    sendmsg->payload.cmd.data.setPinBox.error_pin_buf_len;

		if (img_len > RESOURCE_BUFFER_SIZE ||
		    img_len != normal_pin_buf_len + error_pin_buf_len ||
		    img_len < normal_pin_buf_len ||
		    img_len < error_pin_buf_len) {

			STORE_TA_ERROR("%s Invalid PIN Box image!", LOG_TAG);
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			break;
		}

		if (sendmsg->payload.cmd.data.setPinBox.space > 0) // 21
			g_pinbox_space = sendmsg->payload.cmd.data.setPinBox.space * (screenWidth / STANDARD_SCREEN_WIDTH_DP);

#ifdef ENABLE_USER_PINBOX
		if (normal_pin_buf_len > 0) {
			ret = loadPinpadImage(RES_NORMAL_PINBOX,
					 sendmsg->payload.cmd.data.setPinBox.
					 images.buf, normal_pin_buf_len, 0,
					 sendmsg->payload.cmd.data.setPinBox.
					 y_dp);
			if (0 != ret) {
				break;
			}

		} else {
			//TTY_LOG("%s normal pin is null %d", LOG_TAG, sizes[IDX_NORMAL_PINBOX]);
		}

		if (error_pin_buf_len > 0) {
			ret = loadPinpadImage(RES_ERROR_PINBOX,
					 sendmsg->payload.cmd.data.setPinBox.
					 images.buf + normal_pin_buf_len,
					 error_pin_buf_len, 0,
					 sendmsg->payload.cmd.data.setPinBox.
					 y_dp);
		} else {
			//STORE_TA_ERROR("%s error pin is null", LOG_TAG);
		}
#endif

		if (sendmsg->payload.cmd.data.setPinBox.pinbox_width_dp > 0) // 123
			g_pinbox_width =
			    sendmsg->payload.cmd.data.setPinBox.
			    pinbox_width_dp * screenWidth / STANDARD_SCREEN_WIDTH_DP;

		if (sendmsg->payload.cmd.data.setPinBox.pinbox_height_dp > 0)
#if defined(SUPPORT_DISPLAY_HIDE_NOTCH)
			g_pinbox_height =
			    sendmsg->payload.cmd.data.setPinBox.
			    pinbox_height_dp * (screenHeight - bottomBarHeight - softkeyHeight) / (STANDARD_SCREEN_HEIGHT_DP - STANDARD_BOTTOM_BAR_HEIGHT_DP - STANDARD_SOFTKEY_HEIGHT_DP);
#elif defined(SUPPORT_DUAL_LCD_MAIN)
			g_pinbox_height =
			    sendmsg->payload.cmd.data.setPinBox.
			    pinbox_height_dp * (screenWidth - softkeyHeight) / (STANDARD_SCREEN_WIDTH_DP - STANDARD_SOFTKEY_HEIGHT_DP);
#else
			g_pinbox_height =
			    sendmsg->payload.cmd.data.setPinBox.
			    pinbox_height_dp * (screenHeight - softkeyHeight) / (STANDARD_SCREEN_HEIGHT_DP - STANDARD_SOFTKEY_HEIGHT_DP);
#endif
		if (g_loadable_img[RES_NORMAL_PINBOX].pointer != NULL)
			g_pinbox_y =
			    g_loadable_img[RES_NORMAL_PINBOX].y +
			    g_loadable_img[RES_NORMAL_PINBOX].height -
			    g_pinbox_height; // 687
#if defined(SUPPORT_DUAL_LCD_MAIN)
		TTY_LOG("%s : g_pinbox_space %d, g_pinbox_width %d, g_pinbox_height %d, g_pinbox_y %d", LOG_TAG, g_pinbox_space, g_pinbox_width, g_pinbox_height, g_pinbox_y);
#endif
		break;

	case SPAY_TUI_CMD_SET_IMAGE:	// set backgroung image
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		if ((getPinpadState() != SPAY_TUI_ST_NONE) &&
		    ((getPinpadState() & _SPAY_TUI_ST_START) != 0)) {
			STORE_TA_ERROR("%s Cannot set background, wrong state: 0x%x!",
				LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		img_len = sendmsg->payload.cmd.data.setImage.image.len;

		if (img_len == 0 || img_len > RESOURCE_BUFFER_SIZE) {
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			break;
		}

#ifdef ENABLE_SET_BACKGROUND
		ret = loadPinpadImage(RES_BACKGROUND,
				 sendmsg->payload.cmd.data.setImage.image.buf,
				 img_len,
				 sendmsg->payload.cmd.data.setImage.x_dp,
				 sendmsg->payload.cmd.data.setImage.y_dp);
#endif
		break;

	case SPAY_TUI_CMD_SET_MODE_TEXT:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		if (getPinpadState() != SPAY_TUI_ST_NONE &&
		    ((getPinpadState() & _SPAY_TUI_ST_START) != 0)) {

			STORE_TA_ERROR("%s Cannot set secure text, wrong state: 0x%x!",
				LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		img_len = sendmsg->payload.cmd.data.setSecureModeText.image.len;

		if (img_len == 0 || img_len > RESOURCE_BUFFER_SIZE) {
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			break;
		}

		ret = loadPinpadImage(RES_SECURE_MODE_TEXT,
				 sendmsg->payload.cmd.data.setSecureModeText.
				 image.buf, img_len, 0, PINPAD_SECURE_MODE_TEXT_TOP);

		break;

	case SPAY_TUI_CMD_SET_ACTIONBAR_TEXT:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		if ((getPinpadState() != SPAY_TUI_ST_NONE) &&
		    ((getPinpadState() & _SPAY_TUI_ST_START) != 0)) {

			STORE_TA_ERROR("%s Cannot set action bar text, wrong state: 0x%x!",
			     LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		img_len = sendmsg->payload.cmd.data.setActionBarText.image.len;

		if (img_len == 0 || img_len > RESOURCE_BUFFER_SIZE) {
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			break;
		}

		DBG_LOG("%s action bar buffer = %u", LOG_TAG, img_len);

		ret = loadPinpadImage(RES_ACTION_BAR_TEXT,
				 sendmsg->payload.cmd.data.setActionBarText.
				 image.buf, img_len, PINPAD_ACTION_BAR_TEXT_LEFT_DP,
				 0);

		break;

	case SPAY_TUI_CMD_SET_CANCEL_IMAGE:	// set cancel image
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		if (getPinpadState() != SPAY_TUI_ST_NONE) {
			STORE_TA_ERROR("%s Cannot set cancel button image, wrong state: 0x%x!",
			     LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		img_len = sendmsg->payload.cmd.data.setCancel.images.len;
		press_len = sendmsg->payload.cmd.data.setCancel.pressed_len;
		unpress_len = sendmsg->payload.cmd.data.setCancel.unpressed_len;

		if (img_len == 0 ||
		    img_len > RESOURCE_BUFFER_SIZE ||
		    (img_len != unpress_len + press_len) ||
		    img_len < unpress_len || img_len < press_len) {
			STORE_TA_ERROR("%s Invalid parameters to set cancel button",
				LOG_TAG);
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			break;
		}

#ifdef ENABLE_CANCEL_BUTTON
		if (unpress_len > 0) {
			ret = loadPinpadImage(RES_CANCEL_BUTTON,
					 sendmsg->payload.cmd.data.setCancel.
					 images.buf, unpress_len,
					 sendmsg->payload.cmd.data.setCancel.
					 x_dp,
					 sendmsg->payload.cmd.data.setCancel.
					 y_dp);
			if (ret)
				break;
		}

		if (press_len > 0) {
			ret = loadPinpadImage(RES_CANCEL_BUTTON_PRESSED,
					 sendmsg->payload.cmd.data.setCancel.
					 images.buf + unpress_len, press_len,
					 sendmsg->payload.cmd.data.setCancel.
					 x_dp,
					 sendmsg->payload.cmd.data.setCancel.
					 y_dp);
			if (0 == ret) {
				sPinPadKey_t *cancel =
				    &(g_pinpad_layout.buttons_array[12]);
				cancel->xLeft =
				    g_loadable_img[RES_CANCEL_BUTTON_PRESSED].x;
				cancel->xRight =
				    g_loadable_img[RES_CANCEL_BUTTON_PRESSED].x
				    +
				    g_loadable_img
				    [RES_CANCEL_BUTTON_PRESSED].width;
				cancel->yTop =
				    g_loadable_img[RES_CANCEL_BUTTON_PRESSED].y;
				cancel->yBottom =
				    g_loadable_img[RES_CANCEL_BUTTON_PRESSED].y
				    +
				    g_loadable_img
				    [RES_CANCEL_BUTTON_PRESSED].height;

				////TTY_LOG("%s Cancel %d, %d, %d, %d", LOG_TAG, cancel->xLeft, cancel->xRight, cancel->yTop, cancel->yBottom);
			}
		}
#endif

		break;

	case SPAY_TUI_CMD_SET_PROMPT:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		if ((getPinpadState() != SPAY_TUI_ST_NONE) &&
		    ((getPinpadState() & _SPAY_TUI_ST_START) != 0)) {

			STORE_TA_ERROR("%s Cannot set prompt image, wrong state: 0x%x!",
			     LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		img_len = sendmsg->payload.cmd.data.setPrompt.image.len;

		if (img_len == 0 || img_len > RESOURCE_BUFFER_SIZE) {
			STORE_TA_ERROR("%s invalid arguments", LOG_TAG);
			ret = TIMA_ERROR_INVALID_ARGUMENT;
			break;
		}

		ret = loadPinpadImage(sendmsg->payload.cmd.data.setPrompt.
			       above_pin_box ? RES_PROMPT_ABOVE_PINBOX :
			       RES_PROMPT_BELOW_PINBOX,
			       sendmsg->payload.cmd.data.setPrompt.image.buf,
			       img_len, 0,
			       sendmsg->payload.cmd.data.setPrompt.
			       dis_to_pinbox_dp);
		break;

	case SPAY_TUI_CMD_SETUP: {
		// PIN setup logic
		TTY_LOG("%s PIN Setup", LOG_TAG);
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		ret = setupPin(sendmsg->payload.cmd.data.setup.so.buf,
					sendmsg->payload.cmd.data.setup.so.len, tmp_buf);
		break;
	}

	case SPAY_TUI_CMD_RESUME:	// resume the flow
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		//sendmsg->payload.cmd.data.resume.update_display_only = 1;

		DBG_LOG("%s Resume called: %d", LOG_TAG,
			sendmsg->payload.cmd.data.resume.update_display_only);
		if (0 == sendmsg->payload.cmd.data.resume.update_display_only) {
			// normal case
			switch (getPinpadState()) {
			case SPAY_TUI_ST_SETUP_ENTER_FIRST:
				launchPinpad(false, TYPE_NORMAL_PINBOX);
				setPinpadState(SPAY_TUI_ST_SETUP_START_SECOND);
				g_display_normal_pinbox = true;
				break;
			case SPAY_TUI_ST_SETUP_ENTER_INVALID:
				launchPinpad(false, TYPE_ERROR_PINBOX);
				setPinpadState(SPAY_TUI_ST_SETUP_START_FIRST);
				g_display_normal_pinbox = false;
				break;
			case SPAY_TUI_ST_SETUP_UNMATCH:
				launchPinpad(false, TYPE_ERROR_PINBOX);
				setPinpadState(SPAY_TUI_ST_SETUP_START_SECOND);
				g_display_normal_pinbox = false;
				break;
			case SPAY_TUI_ST_VERIFY_UNMATCH:
				launchPinpad(false, TYPE_ERROR_PINBOX);
				g_display_normal_pinbox = false;
				setPinpadState(SPAY_TUI_ST_VERIFY_START);
				break;
			default:
				STORE_TA_ERROR("%s Invalid state to resume 0x%x 0x%x",
					LOG_TAG, getPinpadState(),
					SPAY_TUI_ST_SETUP_ENTER_FIRST);
				ret = SPAY_TPP_ERROR_INVALID_STATE;
				break;
			}
		} else {
			// special case: only update display
			if (getPinpadState() == SPAY_TUI_ST_VERIFY_UNMATCH) {
				DBG_LOG("%s only update display", LOG_TAG);
				ret = updatePinpadScreen(TYPE_ERROR_PINBOX);
				if (ret == TIMA_ERROR_TUI_DISPLAY_ERROR) {
					ret = SPAY_TPP_ERROR_INVALID_STATE;
				}
			} else {
				STORE_TA_ERROR("%s Invalid state to resume: 0x%x",
					LOG_TAG, getPinpadState());
				ret = SPAY_TPP_ERROR_INVALID_STATE;
			}
		}

		break;

	case SPAY_TUI_CMD_VERIFY:{
			respmsg->spayhdr.header.len = sizeof(commonRsp_t);

			if (getPinpadState() != SPAY_TUI_ST_NONE &&
				getPinpadState() != SPAY_TUI_ST_VERIFY_UNMATCH) {

				STORE_TA_ERROR("%s Invalid state to verify 0x%x!",
					LOG_TAG, getPinpadState());
				ret = SPAY_TPP_ERROR_INVALID_STATE;
				break;
			}
			DBG_LOG("%s Cmd: verify PIN", LOG_TAG);
#if !defined(WIN32)
			if (0 == gl_min_pin_len || gl_min_pin_len > PIN_SIZE) {
				STORE_TA_ERROR("%s invalid number of PIN: %d", LOG_TAG,
					gl_min_pin_len);
				ret = SPAY_TPP_ERROR_INVALID_NUMBER_OF_PIN;
				break;
			}

			if (gl_pin_old_len == 0) {
				STORE_TA_ERROR("%s no PIN to verify", LOG_TAG);
				ret = SPAY_TPP_ERROR_NO_PIN;
				break;
			}

			if(gl_pin_old_len > PIN_SIZE) {
				STORE_TA_ERROR("%s gl_pin_old_len is invalid", LOG_TAG);
				ret = TIMA_ERROR_OUT_OF_MEMORY;
				break;
			}
			gl_pin_verify_len = gl_pin_old_len;
			memcpy(gl_pin_verify, gl_pin_old, gl_pin_old_len);
#endif
			DBG_LOG
			    ("%s Verify called with update_display_only=%d, close_tui_session=%d",
			     LOG_TAG,
			     sendmsg->payload.cmd.data.verify.
			     update_display_only,
			     sendmsg->payload.cmd.data.verify.
			     close_tui_session);

			if (sendmsg->payload.cmd.data.verify.
			    update_display_only) {
				ret = launchPinpad(true, TYPE_DISABLED_PINBOX);
				g_display_normal_pinbox = false;
			} else {
				ret = launchPinpad(true, TYPE_NORMAL_PINBOX);
				g_display_normal_pinbox = true;
			}

			if (ret) {
				STORE_TA_ERROR("%s Failed to launch TUI!", LOG_TAG);
				break;
			}

			g_close_tui_after_verify =
			    sendmsg->payload.cmd.data.verify.close_tui_session;

			if (sendmsg->payload.cmd.data.verify.
			    update_display_only) {
				setPinpadState(SPAY_TUI_ST_VERIFY_UNMATCH);
				DBG_LOG("%s Update state to 0x%0x", LOG_TAG, getPinpadState());
				g_display_normal_pinbox = true;	// next time when resume, display normal pinbox for once
			} else {
				setPinpadState(SPAY_TUI_ST_VERIFY_START);
				DBG_LOG("%s Update state to 0x%0x", LOG_TAG, getPinpadState());
			}

			break;
		}

	case SPAY_TUI_CMD_GET_PIN_RANDOM_FOR_WALLET:{
		TTY_LOG("%s :: Processing cmd SPAY_TUI_CMD_GET_PIN_RANDOM_FOR_WALLET\n ", LOG_TAG);
		respmsg->spayhdr.header.len = sizeof(getSecureResultRsp_t);

		g_nonce_len = sendmsg->payload.cmd.data.getSecureResultForWallet.wallet_nonce.len;
		if ((g_nonce_len <= 0) || (g_nonce_len > NONCE_BUFFER_SIZE)) {
			DBG_LOG("%s invalid  Wallet nonce_len: %u", LOG_TAG, g_nonce_len);
			g_nonce_len = 0;
			ret = TIMA_ERROR_OUT_OF_MEMORY;
			break;
		}

		memcpy(g_nonce, sendmsg->payload.cmd.data.getSecureResultForWallet.wallet_nonce.buf, g_nonce_len);

		g_ta_name_len = sendmsg->payload.cmd.data.getSecureResultForWallet.app_name.len;
		if (g_ta_name_len >= APP_NAME_BUFFER_SIZE) {
			ret = TIMA_ERROR_OUT_OF_MEMORY;
			break;
		}

		memcpy(g_ta_name, sendmsg->payload.cmd.data.getSecureResultForWallet.app_name.buf, g_ta_name_len);
		DBG_LOG("%s App Name len = %u, App name = %s", LOG_TAG, g_ta_name_len, g_ta_name);

		respmsg->payload.rsp.data.getSecureResultRsp.auth_result_src =  SPAY_AUTH_RESULT_SRC_TUI;
		ret = wrapRandom(respmsg);

		break;
	}
	case SPAY_TUI_CMD_GET_SECURE_RESULT:	// get PIN verification result
		respmsg->spayhdr.header.len = sizeof(getSecureResultRsp_t);

		if (getPinpadState() != SPAY_TUI_ST_VERIFY_MATCH) {
			STORE_TA_ERROR("%s Invalid state to get result 0x%x!", LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		setPinpadState(SPAY_TUI_ST_SECURE_RESULT);
		clearPinData();

		if (isPinpadTimerExpired()) {
			STORE_TA_ERROR
			    ("%s TUI verification result is not valid anymore. Please re-authenticate",
			     LOG_TAG);
			ret = SPAY_TPP_ERROR_TUI_VERIFIED_TIMEOUT;
			break;
		}

		g_nonce_len = sendmsg->payload.cmd.data.getSecureResult.nonce.len;
		if ((g_nonce_len <= 0) || (g_nonce_len > NONCE_BUFFER_SIZE)) {
                        DBG_LOG("%s invalid g_nonce_len: %u", LOG_TAG, g_nonce_len);
                        g_nonce_len = 0;
			ret = TIMA_ERROR_OUT_OF_MEMORY;
			break;
		}
		memcpy(g_nonce, sendmsg->payload.cmd.data.getSecureResult.nonce.buf, g_nonce_len);

		g_ta_name_len = sendmsg->payload.cmd.data.getSecureResult.app_name.len;
		if (g_ta_name_len >= APP_NAME_BUFFER_SIZE) {
			ret = TIMA_ERROR_OUT_OF_MEMORY;
			break;
		}

		memcpy(g_ta_name, sendmsg->payload.cmd.data.getSecureResult.app_name.buf, g_ta_name_len);
		respmsg->payload.rsp.data.getSecureResultRsp.auth_result_src = SPAY_AUTH_RESULT_SRC_TUI;
		DBG_LOG("%s App Name len = %u, App name = %s", LOG_TAG, g_ta_name_len, g_ta_name);

		ret = wrapPinWithResult(respmsg, SPAY_AUTH_RESULT_SRC_TUI);
		break;

	case SPAY_TUI_CMD_GET_RANDOM:
		respmsg->spayhdr.header.len = sizeof(getSecureResultRsp_t);

		g_ta_name_len = sendmsg->payload.cmd.data.getRandom.app_name.len;
		if (g_ta_name_len >= APP_NAME_BUFFER_SIZE) {
			ret = TIMA_ERROR_OUT_OF_MEMORY;
			break;
		}

		memcpy(g_ta_name, sendmsg->payload.cmd.data.getRandom.app_name.buf, g_ta_name_len);
		DBG_LOG("%s App Name len = %u, App name = %s", LOG_TAG, g_ta_name_len, g_ta_name);

		ret = wrapPinAndRandom(respmsg);
		break;

	case SPAY_AUTH_CMD_GET_NONCE:{
			TTY_LOG("%s Get nonce", LOG_TAG);
			respmsg->spayhdr.header.len = sizeof(getNonceRsp_t);

			uint32_t in_out_nonce_len = sendmsg->payload.cmd.data.getNonce.nonce_len;
			if (in_out_nonce_len > NONCE_BUFFER_SIZE) {
				STORE_TA_ERROR ("%s nonce buffer is less than required length %u", LOG_TAG, in_out_nonce_len);
				ret = TIMA_ERROR_OUT_OF_MEMORY;
				break;
			}

			uint8_t *out_nonce = respmsg->payload.rsp.data.getNonceRsp.nonce.buf;
			ret = get_nonce(&in_out_nonce_len, out_nonce);
			respmsg->payload.rsp.data.getNonceRsp.nonce.len = in_out_nonce_len;
			respmsg->payload.rsp.data.getNonceRsp.ret_comm.retCode = ret;
			break;
		}

	case SPAY_AUTH_CMD_GET_AUTH_RESULT:{
			uint32_t auth_src = SPAY_AUTH_RESULT_SRC_UNKNOWN;

			respmsg->spayhdr.header.len =  sizeof(getAuthResultRsp_t);
			DBG_LOG("size of getAuthResultRsp_t=%d", sizeof(getAuthResultRsp_t));

			if (getPinpadState() != SPAY_TUI_ST_NONE &&
				getPinpadState() != SPAY_TUI_ST_SECURE_RESULT) {

				STORE_TA_ERROR("%s Invalid state to get auth result 0x%x!", LOG_TAG, getPinpadState());
				ret = SPAY_TPP_ERROR_INVALID_STATE;
				break;
			}
			// first verify secure object, which could come from TUI or FP or IRIS

			uint32_t so_len =
			    sendmsg->payload.cmd.data.getAuthResult.so.len;
			if (so_len > SO_BUFFER_SIZE) {
				STORE_TA_ERROR("%s Secure object is too big (%u)",
					LOG_TAG, so_len);
				ret = TIMA_ERROR_OUT_OF_MEMORY;
				break;
			}
			DBG_LOG("%s Secure object len (%u)", LOG_TAG, so_len);

			if (so_len > 0) {
				memcpy(tmp_buf,
					   sendmsg->payload.cmd.data.getAuthResult.so.buf,
					   so_len);
			}
#ifdef USE_QSEE
			ret = authenticate_transaction(tmp_buf,
						       so_len, (uint8_t *)
						       QSEE_SPAYTUI_PROCESS,
						       strlen
						       (QSEE_SPAYTUI_PROCESS));
#elif defined(USE_BF)
            ret = authenticate_transaction(tmp_buf, so_len
                        , (uint8_t *)TEEGRIS_TUI_PROCESS
                        , strlen(TEEGRIS_TUI_PROCESS));
#else
			ret = authenticate_transaction(tmp_buf,
						       so_len, (uint8_t *)
						       TBASE_SPAYTUI_PROCESS,
						       strlen
						       (TBASE_SPAYTUI_PROCESS));
#endif

			if (ret != 0) {
				break;
			}
			auth_src = get_auth_source();
			DBG_LOG("auth_src=%u", auth_src);

			// further check state and source and timing
			if (auth_src == SPAY_AUTH_RESULT_SRC_TUI &&
				getPinpadState() != SPAY_TUI_ST_SECURE_RESULT) {

				STORE_TA_ERROR("%s Auth src (TUI) and state (0x%0x) mismatch!",
				     LOG_TAG, getPinpadState());
				ret = SPAY_TPP_ERROR_INVALID_STATE;
				break;
			}

			respmsg->payload.rsp.data.getSecureResultRsp.
			    auth_result_src = auth_src;

			g_nonce_len = sendmsg->payload.cmd.data.getAuthResult.nonce.len;
			if ((g_nonce_len <= 0) || (g_nonce_len > NONCE_BUFFER_SIZE)) {
                                DBG_LOG("%s invalid g_nonce_len: %u", LOG_TAG, g_nonce_len);
                                g_nonce_len = 0;
				ret = TIMA_ERROR_OUT_OF_MEMORY;
				break;
			}
			memcpy(g_nonce,
			       sendmsg->payload.cmd.data.getAuthResult.nonce.
			       buf, g_nonce_len);

			g_ta_name_len = sendmsg->payload.cmd.data.getAuthResult.app_name.len;
			if (g_ta_name_len >= APP_NAME_BUFFER_SIZE) {
				ret = TIMA_ERROR_OUT_OF_MEMORY;
				break;
			}

			memcpy(g_ta_name,
			       sendmsg->payload.cmd.data.getAuthResult.app_name.
			       buf, g_ta_name_len);

			// wrap auth result
			ret = wrapPinWithResult(respmsg, auth_src);

			setPinpadState(SPAY_TUI_ST_NONE);
			setPinpadTimer();
			g_wallet_bio_auth = true;

			respmsg->payload.rsp.data.getAuthResultRsp.need_update_pin_so = 0;	// no PIN SO update

			break;
		}

	case SPAY_TUI_CMD_CLOSE:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);
		DBG_LOG("%s close TUI", LOG_TAG);
		ret = closeTuiSession();

		break;

	case SPAY_TUI_CMD_CHECK_TUI_SESSION:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);
		if (getPinpadState() == SPAY_TUI_ST_NONE ||
			getPinpadState() == SPAY_TUI_ST_CANCELLED ||
			getPinpadState() == SPAY_TUI_ST_SETUP_MATCH ||
		    (getPinpadState() == SPAY_TUI_ST_VERIFY_MATCH
		     && g_close_tui_after_verify)) {
			ret = SPAY_TPP_TUI_SESSION_OFF;
			DBG_LOG("%s getPinpadState() = 0x%x, TUI session is off", LOG_TAG, getPinpadState());
		} else {
			ret = SPAY_TPP_TUI_SESSION_ON;
			DBG_LOG("%s getPinpadState() = 0x%x, TUI session is on", LOG_TAG, getPinpadState());
		}
		break;

	case SPAY_TUI_CMD_CLEAR_STATE:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);

		setPinpadState(SPAY_TUI_ST_NONE);
		clearPinData();
		ret = 0;
		break;

	case SPAY_TUI_CMD_CHECK_RET_CODE:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);
#if !defined(USE_MOBICORE) && !defined(USE_BF)
		ret = getReturnCode();
#endif
		break;

	case SPAY_AUTH_CMD_SET_PREFERENCE:
		respmsg->spayhdr.header.len = sizeof(commonRsp_t);
		DBG_LOG("%s set RTL==%d", LOG_TAG, sendmsg->payload.cmd.data.setPreference.val.isRTL);
		DBG_LOG("%s set BHR==%d", LOG_TAG, sendmsg->payload.cmd.data.setPreference.val.isBHR);
		g_preference.isRTL = sendmsg->payload.cmd.data.setPreference.val.isRTL;
		g_preference.isBHR = sendmsg->payload.cmd.data.setPreference.val.isBHR;
		break;

	default:
		STORE_TA_ERROR("%s Unknown command 0x%x", LOG_TAG, cmd_id);
		ret = TIMA_ERROR_TUI_UNKNOWN_CMD;
		break;

	}

	switch (ret) {
	case SPAY_TPP_SETUP_PIN_VERIFIED:
	case SPAY_TPP_ERROR_CANCELED:
	case SPAY_TPP_ERROR_INVALID_STATE:
	case TIMA_ERROR_TUI_CANCELLED:
		clearPinData();
		clearLoadableResource();
		break;
	case SPAY_TPP_PIN_VERIFYED:
		if (g_close_tui_after_verify) {
			clearPinData();
			clearLoadableResource();
		}
		break;
	default:
		break;
	}
	return ret;
}


uint32_t setupPin(
	uint8_t * so_buf,
	uint32_t so_len,
	uint8_t * tmp_buf
) {

	uint32_t ret = TIMA_SUCCESS;
	do {
		if (getPinpadState() != SPAY_TUI_ST_NONE &&
			getPinpadState() != SPAY_TUI_ST_VERIFY_MATCH) {
			STORE_TA_ERROR("%s Cannot start TUI session, wrong state: 0x%x!\n", LOG_TAG, getPinpadState());
			ret = SPAY_TPP_ERROR_INVALID_STATE;
			break;
		}

		if (getPinpadState() == SPAY_TUI_ST_VERIFY_MATCH
			&& isPinpadTimerExpired()) {
			STORE_TA_ERROR("%s TUI verification result expired. Please re-authenticate", LOG_TAG);
			ret = SPAY_TPP_ERROR_TUI_VERIFIED_TIMEOUT;
			setPinpadState(SPAY_TUI_ST_NONE);
			break;
		}

		if (gl_min_pin_len < US_PIN_SIZE
			|| gl_min_pin_len > PIN_SIZE) {
			STORE_TA_ERROR("%s invalid number of PIN: %d", LOG_TAG, gl_min_pin_len);
			ret = SPAY_TPP_ERROR_INVALID_NUMBER_OF_PIN;
			break;
		}

		DBG_LOG("so_len = %u", so_len);
		DBG_LOG("%s gl_pin_old_len %u", LOG_TAG, gl_pin_old_len);

		if (so_len > SO_BUFFER_SIZE) {
			STORE_TA_ERROR("%s Invalid secure object len for PIN setup!", LOG_TAG);
			ret = SPAY_TPP_ERROR_INVALID_PIN_SO;
			break;
		} else {
			if (so_len > 0) {
				memcpy(tmp_buf, so_buf, so_len);
			}
		}
		DBG_LOG("%s gl_pin_old_len %u", LOG_TAG, gl_pin_old_len);

		if (getPinpadState() == SPAY_TUI_ST_NONE) {
			ret = launchPinpad(true, TYPE_NORMAL_PINBOX);
		} else {	// getPinpadState() == SPAY_TUI_ST_VERIFY_MATCH
			if (g_close_tui_after_verify)
				// verified but TUI is closed
				ret = launchPinpad(true, TYPE_NORMAL_PINBOX);
			else
				ret = launchPinpad(false, TYPE_NORMAL_PINBOX);
		}
		g_display_normal_pinbox = true;

		if (ret) {
			STORE_TA_ERROR("%s Failed to launch TUI!", LOG_TAG);
			break;
		}

		setPinpadState(SPAY_TUI_ST_SETUP_START_FIRST);
		break;
	} while (0);

	return ret;
}

/*
 * Validate if the PIN entered is allowed or not
 */
uint32_t validatePin() {
	int32_t consecutive = 0;
	uint32_t same = 1;
	int i;

	for (i = 1; i < g_pin_length; i++) {
		if (g_pin[i - 1] == g_pin[i]) {
			same++;
			consecutive = 0;
			if (same == INVALID_NUMBER_OF_SAME_DIGITS) {
				return SPAY_TPP_ERROR_3_SAME_DIGITS;
			}
		} else if (g_pin[i - 1] - 1 == g_pin[i]
			&& consecutive == -(i - 1)) {
			consecutive--;
			same = 1;
		} else if (g_pin[i - 1] + 1 == g_pin[i] && consecutive == i - 1) {
			consecutive++;
			same = 1;
		} else {
			consecutive = 0;
			same = 1;
		}
		//TTY_LOG("%s same=%d, consecutive=%d, i=%d", LOG_TAG, same, consecutive, i);
	}

	if (consecutive == i - 1 || consecutive == -(i - 1)) {
		return SPAY_TPP_ERROR_CONSECUTIVE_DIGITS;
	}

	return 0;
}

// Generate a random number for TUI. This random number should only be generated once unless there is factory reset.
uint32_t generateRandom() {
	uint8_t random[SPAY_RANDOM_NUMBER_SIZE] = { 0 };
	uint32_t rand_len = sizeof(random);

	initPinpadAndData();
	if (TZ_gen_rand_data(random, &rand_len) != 0 ||
		rand_len != sizeof(random)) {
		TTY_LOG("%s Failed to generate random number for the PIN!", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	if (!set_pin_random(random, rand_len)) {
		TTY_LOG("%s Error loading pin random", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}
	return 0;
}

uint32_t wrapRandom(spayTuiMsg_t * respmsg) {
	uint8_t pin_random[SPAY_RANDOM_NUMBER_SIZE] = { 0 };
	uint32_t pin_random_len = sizeof(pin_random);

	uint32_t ret = 0;
	spayVerifyPinResult_t wallet_result;
	uint64_t current_ts = 0;

	if (!is_pin_random_loaded()) {
		TTY_LOG("%s random is not loaded!", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	memset(&wallet_result, 0 , sizeof(spayVerifyPinResult_t));

	if (!get_pin_random(pin_random, &pin_random_len)) {
		TTY_LOG("%s Error retrieving the pin random", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}
	wallet_result.nonce_len = g_nonce_len;
	memcpy(wallet_result.nonce, g_nonce, g_nonce_len);

	memcpy(wallet_result.rand, pin_random, pin_random_len);
	memcpy(wallet_result.magic, TUI_RESULT_MAGIC, sizeof(wallet_result.magic));

	if ((getPinpadState() == SPAY_TUI_ST_SECURE_RESULT) || g_wallet_bio_auth) {

		if (isPinpadTimerExpired()) {
			TTY_LOG("%s TUI verification result is not valid anymore. Please re-authenticate", LOG_TAG);
			g_wallet_bio_auth = false;
			wallet_result.result = SPAY_AUTH_FAIL;
		} else if (TZ_API_OK != TZ_get_secure_timestamp(&current_ts)) {
				TTY_LOG("Error to get secure timestamp");
				wallet_result.result = SPAY_AUTH_FAIL;
		} else {
			wallet_result.ts = current_ts;
			wallet_result.app_name_len = g_ta_name_len;
			memcpy(wallet_result.app_name, g_ta_name, g_ta_name_len);
			wallet_result.auth_src = SPAY_AUTH_RESULT_SRC_TUI;
			wallet_result.result = SPAY_TUI_PIN_VERIFY_SUCCESS;
		}
	} else {
		wallet_result.result = SPAY_AUTH_FAIL;
	}

	uint32_t wrap_len = SPAY_VERIFY_PIN_SO_SIZE;
	if (0 != (ret = wrapDataForBcCore(g_ta_name, g_ta_name_len, (uint8_t *) & wallet_result, sizeof(wallet_result),
				  respmsg->payload.rsp.data.getSecureResultRsp.result_so.buf, &wrap_len))) {
		// failed to encapsulate
		TTY_LOG("%s unable to encapsulate authentication result, ret = %u", LOG_TAG, ret);
		ret = TIMA_ERROR_TUI_WRAP_ERROR;
		respmsg->payload.rsp.data.getSecureResultRsp.result_so.len = 0;
	} else {
		respmsg->payload.rsp.data.getSecureResultRsp.result_so.len = wrap_len;
		DBG_LOG("%s wrapped len: %u", LOG_TAG, wrap_len);
	}
	return ret;
}

// Wrap PIN verification result for another TA
uint32_t wrapPinWithResult(spayTuiMsg_t * respmsg, uint32_t auth_src) {
	uint8_t pin_random[SPAY_RANDOM_NUMBER_SIZE] = { 0 };
	uint32_t pin_random_len = sizeof(pin_random);

	uint32_t ret = 0;
	//spayVerifyPinResult_t pin_result;
	inappVerifyPinResult_t pin_result;
	uint32_t pin_result_len = 0;
	uint64_t current_ts = 0;
	uint32_t wrap_len = 0;

	memset(&pin_result, 0, sizeof(pin_result));

	if (!is_pin_random_loaded()) {
		TTY_LOG("%s random is not loaded!", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	pin_result.pin_result.nonce_len = g_nonce_len;
	memcpy(pin_result.pin_result.nonce, g_nonce, g_nonce_len);

	pin_result.pin_result.app_name_len = g_ta_name_len;
	memcpy(pin_result.pin_result.app_name, g_ta_name, g_ta_name_len);

	DBG_LOG("getPinpadState() = 0x%x, get_auth_result() = %u, get_auth_inapp() = %d",
			getPinpadState(), get_auth_result(), get_auth_inapp());

	pin_result.pin_result.result = SPAY_TUI_PIN_VERIFY_SUCCESS;
	pin_result.pin_result.auth_src = auth_src;

	memcpy(pin_result.pin_result.magic, TUI_RESULT_MAGIC, sizeof(pin_result.pin_result.magic));

	if (!get_pin_random(pin_random, &pin_random_len)) {
		TTY_LOG("%s Error retrieving the pin random", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	memcpy(pin_result.pin_result.rand, pin_random, pin_random_len);

	if (TZ_API_OK != TZ_get_secure_timestamp(&current_ts)) {
		TTY_LOG("%s Error to get secure timestamp", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}
	DBG_LOG("secure timestamp: %lld", current_ts);
	pin_result.pin_result.ts = current_ts;

	DBG_LOG("Wrap_pin_verify_result:");
	DBG_DUMP(pin_random, pin_random_len);
	DBG_DUMP(pin_result.pin_result.rand, pin_random_len);

	pin_result_len = sizeof(spayVerifyPinResult_t);
	wrap_len = SPAY_VERIFY_PIN_SO_SIZE;

	DBG_LOG("pin_result_len=%u wrap_len=%u", pin_result_len, wrap_len);

	if (0 != (ret = wrapDataForBcCore(g_ta_name, g_ta_name_len, (uint8_t *) & pin_result, pin_result_len,
				  respmsg->payload.rsp.data.getSecureResultRsp.result_so.buf, &wrap_len))) {
		// failed to encapsulate
		TTY_LOG("%s unable to encapsulate authentication result, ret = %u", LOG_TAG, ret);
		ret = TIMA_ERROR_TUI_WRAP_ERROR;
		respmsg->payload.rsp.data.getSecureResultRsp.result_so.len = 0;
	} else {
		respmsg->payload.rsp.data.getSecureResultRsp.result_so.len =
		    wrap_len;
		DBG_LOG("%s wrapped len: %u", LOG_TAG, wrap_len);
	}
	return ret;
}

/*
 *  Wrap the random number associated with PIN. The return data structure is the same as getSecureResult.
 *  However, we don't set the nonce and we don't set the verification result field. Payment TA should know
 *  this can only be used when authentication is not needed.
 */
uint32_t wrapPinAndRandom(spayTuiMsg_t * respmsg) {
	uint8_t pin_random[SPAY_RANDOM_NUMBER_SIZE] = { 0 };
	uint32_t pin_random_len = sizeof(pin_random);
	uint32_t ret = 0;
	spayVerifyPinResult_t pin_result = { 0 };

	if (!is_pin_random_loaded()) {
		TTY_LOG("%s random is not loaded!", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	memcpy(pin_result.magic, SPAY_TUI_MAGIC_STRING, sizeof(pin_result.magic));

	pin_result.app_name_len = g_ta_name_len;
	memcpy(pin_result.app_name, g_ta_name, g_ta_name_len);

	if (!get_pin_random(pin_random, &pin_random_len)) {
		TTY_LOG("Error retrieving the pin random");
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	memcpy(pin_result.rand, pin_random, pin_random_len);

	uint32_t wrap_len = SPAY_VERIFY_PIN_SO_SIZE;

	if (0 != (ret = wrapDataForBcCore(g_ta_name, g_ta_name_len, (uint8_t *)& pin_result, sizeof(pin_result),
		respmsg->payload.rsp.data.getSecureResultRsp.result_so.buf, &wrap_len))) {
		// failed to encapsulate
		TTY_LOG("%s unable to encapsulate random, ret = %u", LOG_TAG,
			ret);
		ret = TIMA_ERROR_TUI_WRAP_ERROR;
	}

	respmsg->payload.rsp.data.getSecureResultRsp.result_so.len = wrap_len;
	DBG_LOG("%s wrapped len: %u", LOG_TAG, wrap_len);
	return ret;
}

// Wrap the new PIN into a secure object
uint32_t wrapPin(getPinRsp_t * rsp) {
	uint8_t pin_random[SPAY_RANDOM_NUMBER_SIZE] = { 0 };
	uint32_t pin_random_len = sizeof(pin_random);

	if (!is_pin_random_loaded()) {
		TTY_LOG("%s random is not loaded!", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}
	// wrap and return PIN
	uint32_t wrap_len = PIN_FP_SO_SIZE;
	pin_fp_so_t pin_so = { 0 };

	memcpy(pin_so.pin, gl_pin_verify, gl_pin_verify_len);
	pin_so.pin_len = gl_pin_verify_len;

	if (!get_pin_random(pin_random, &pin_random_len)) {
		TTY_LOG("Error retrieving the pin random");
		return TIMA_ERROR_TUI_WRAP_ERROR;
	}

	DBG_LOG("Wrap_pin:");
	DBG_DUMP(pin_random, pin_random_len);

	memcpy(pin_so.rand, pin_random, pin_random_len);

    uint32_t ret = 0;
#ifdef USE_SPU
  ret = SPU_wrap_data((unsigned char *)&pin_so, sizeof(pin_so), rsp->pin_so.buf, &wrap_len);
#else
  ret = TZ_wrap_data((unsigned char *)&pin_so, sizeof(pin_so), rsp->pin_so.buf, &wrap_len);
#endif

	if (ret) {
		TTY_LOG("%s unable to wrap new PIN!", LOG_TAG);
		return TIMA_ERROR_TUI_WRAP_ERROR;
	} else {
		TTY_LOG("%s wrap new PIN, Wrapped len: %u", LOG_TAG, wrap_len);
		rsp->pin_so.len = wrap_len;

		// Make the new PIN ready to use
		gl_pin_old_len = gl_pin_verify_len;
		memcpy(gl_pin_old, gl_pin_verify, gl_pin_old_len);
		memset(gl_pin_verify, 0, gl_pin_verify_len);
		gl_pin_verify_len = 0;
	}
	return TZ_API_OK;
}

// check the secure object is from TUI itself
uint32_t validateSo(uint8_t * so, uint32_t so_len) {
	DBG_LOG("validateSo:");
	uint32_t ret_val = SPAY_TPP_ERROR_INVALID_PIN_SO;
	uint8_t so_source[AUTH_SO_SRC_UUID_LEN(AUTH_SO_SRC_TUI_UUID) + 2] = { 0 };
	uint32_t so_source_len = sizeof(so_source);

	ret_val = TZ_get_so_source(so, so_len, so_source, &so_source_len);
	if (ret_val) {
		TTY_LOG("Error identifying the secure obj source, ret = 0x%X", ret_val);
		return ret_val;
	}

	if (so_source_len != AUTH_SO_SRC_UUID_LEN(AUTH_SO_SRC_TUI_UUID)) {
		TTY_LOG("Cannot verify the source of the secure object, Invalid SO source len");
		return ret_val;
	}

	if (memcmp(so_source, AUTH_SO_SRC_TUI_UUID, AUTH_SO_SRC_UUID_LEN(AUTH_SO_SRC_TUI_UUID))) {
		TTY_LOG("%s Secure object is not from TUI!", LOG_TAG);
		return ret_val;
	}
	return 0;
}

//TODO: move this to common
// Unwrap the PIN secure object and save to global variable
uint32_t unwrapPinSo(spayTuiMsg_t * sendmsg, spayTuiMsg_t * respmsg) {
	DBG_LOG("unwrapPinSo:");
	(void)respmsg;
	uint32_t ret = 0;
	uint32_t pin_so_len = sendmsg->payload.cmd.data.loadPin.pin_so.len;
	pin_fp_so_t pin_so;
	uint32_t pin_so_unwrap_len = PIN_FP_SO_SIZE;
	uint8_t tempbuf[PIN_FP_SO_SIZE];

	if (is_pin_random_loaded()) {
		TTY_LOG("%s PIN already loaded. Request ignored", LOG_TAG);
		return ret;
	}

	if (pin_so_len == 0) {
		TTY_LOG("%s PIN SO len is 0!", LOG_TAG);
		return SPAY_TPP_ERROR_INVALID_PIN_SO;
#ifndef USE_BTS
	} else if (pin_so_len > PIN_FP_SO_SIZE) {
		TTY_LOG("%s PIN SO len too big!", LOG_TAG);
		return SPAY_TPP_ERROR_INVALID_PIN_SO;
#endif
    }
  else {
		// copy sendmsg->payload.cmd.data.loadPin.pin_so.buf to tempbuf
		memcpy(tempbuf, sendmsg->payload.cmd.data.loadPin.pin_so.buf, pin_so_len);
	}

#ifndef USE_SPU
	if (validateSo(tempbuf, pin_so_len)) {
        DBG_LOG("SPAY_TPP_ERROR_INVALID_PIN_SO: %u", pin_so_len);
		return SPAY_TPP_ERROR_INVALID_PIN_SO;
	}
#endif

	gl_pin_old_len = PIN_SIZE;

#ifdef USE_SPU
	ret = SPU_unwrap_data(tempbuf, pin_so_len, (uint8_t *)&pin_so, &pin_so_unwrap_len);
#else
	ret = TZ_unwrap_data(tempbuf, pin_so_len, (uint8_t *)&pin_so, &pin_so_unwrap_len);
#endif
	if (ret) {
		TTY_LOG("%s unable to unwrap, ret = 0x%08x, wrap_len = %u", LOG_TAG, ret, pin_so_len);
		ret = SPAY_TPP_ERROR_INVALID_PIN_SO;
	} else {

		gl_pin_old_len = pin_so.pin_len;
		if (gl_pin_old_len > PIN_SIZE) {
			TTY_LOG("gl_pin_old_len is invalid");
			return TIMA_ERROR_OUT_OF_MEMORY;
		}
#ifndef USE_BTS
		/*
		* unwrap_pin_so is supposed to take pin_fp_so_t as input. However, spayVerifyPinResult_t object
		* could be passed here for unwrap. When spayVerifyPinResult_t is interpreted as pin_fp_so_t,
		* there are some security issues (MPCPII-8121) since nonce and nonce_len of spayVerifyPinResult_t
		* can be controlled by normal world.
		* Check whether it is a spayVerifyPinResult_t object.
		*/
		if (pin_so_unwrap_len <= sizeof(spayVerifyPinResult_t)
			|| strncmp((const char *)&pin_so, SPAY_TUI_MAGIC_STRING, SPAY_TUI_MAGIC_STRING_LEN) == 0
			|| strncmp((const char *)&pin_so, SPAY_PIN_MANAGER_MAGIC_STRING, SPAY_PIN_MANAGER_MAGIC_STRING_LEN) == 0) {
			TTY_LOG("%s Invalid PIN SO: maybe a spayVerifyPinResult_t object!", __func__);
			return SPAY_TPP_ERROR_INVALID_PIN_SO;
		}
#else
    if (pin_so_unwrap_len == sizeof(spayVerifyPinResult_t)
        || strncmp((const char *)&pin_so, SPAY_TUI_MAGIC_STRING, SPAY_TUI_MAGIC_STRING_LEN) == 0
        || strncmp((const char *)&pin_so, SPAY_PIN_MANAGER_MAGIC_STRING, SPAY_PIN_MANAGER_MAGIC_STRING_LEN) == 0) {
        TTY_LOG("%s Invalid PIN SO: maybe a spayVerifyPinResult_t object!", __func__);
        return SPAY_TPP_ERROR_INVALID_PIN_SO;
		}
#endif
		memcpy(gl_pin_old, pin_so.pin, gl_pin_old_len);

		if (!set_pin_random(pin_so.rand, SPAY_RANDOM_NUMBER_SIZE)) {
			TTY_LOG("%sError retrieving pin random", LOG_TAG);
			ret = SPAY_TPP_ERROR_INVALID_PIN_SO;
		}

#ifndef USE_SPU
		if (ret == TZ_API_OK) {
			if (validateSoVersion(tempbuf) == false) {
				respmsg->payload.rsp.data.loadPinRsp.updated_pin_so.len
					= sizeof(respmsg->payload.rsp.data.loadPinRsp. updated_pin_so.buf);

				ret = TZ_wrap_data((uint8_t *)&pin_so, pin_so_unwrap_len,
						respmsg->payload.rsp.data.loadPinRsp.updated_pin_so.buf,
						(uint32_t *)&respmsg->payload.rsp.data.loadPinRsp.updated_pin_so.len);
				if (ret) {
					TTY_LOG("%s Error rewrapping pin object", LOG_TAG);
					ret = SPAY_TPP_TUI_ERROR_REWRAP_PIN_SO;
				} else {
					ret = SPAY_TPP_TUI_UPGRADE_PIN_SO_VERSION;
					TTY_LOG("%s Successfully rewrapped the pin SO file", LOG_TAG);
				}
			}
		}
#endif
	}
	return ret;
}
