/****************************************************************************** * * Copyright 2018 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #define LOG_TAG "NxpEseHal" #include #include "LsClient.h" #include "SecureElement.h" #include "phNxpEse_Api.h" extern bool ese_debug_enabled; namespace android { namespace hardware { namespace secure_element { namespace V1_0 { namespace implementation { sp SecureElement::mCallbackV1_0 = nullptr; static void onLSCompleted(bool result, std::string reason, void* arg) { ((SecureElement*)arg)->onStateChange(result, reason); } SecureElement::SecureElement() : mOpenedchannelCount(0), mOpenedChannels{false, false, false, false} {} Return SecureElement::init( const sp< ::android::hardware::secure_element::V1_0::ISecureElementHalCallback>& clientCallback) { ESESTATUS status = ESESTATUS_SUCCESS; if (clientCallback == nullptr) { return Void(); } else { mCallbackV1_0 = clientCallback; if (!mCallbackV1_0->linkToDeath(this, 0 /*cookie*/)) { ALOGE("%s: Failed to register death notification", __func__); } } if (isSeInitialized()) { clientCallback->onStateChange(true); return Void(); } status = seHalInit(); if (status != ESESTATUS_SUCCESS) { clientCallback->onStateChange(false); return Void(); } LSCSTATUS lsStatus = LSC_doDownload(onLSCompleted, (void*)this); /* * LSC_doDownload returns LSCSTATUS_FAILED in case thread creation fails. * So return callback as false. * Otherwise callback will be called in LSDownload module. */ if (lsStatus != LSCSTATUS_SUCCESS) { ALOGE("%s: LSDownload thread creation failed!!!", __func__); SecureElementStatus sestatus = seHalDeInit(); if (sestatus != SecureElementStatus::SUCCESS) { ALOGE("%s: seHalDeInit failed!!!", __func__); } clientCallback->onStateChange(false); } return Void(); } Return SecureElement::getAtr(getAtr_cb _hidl_cb) { hidl_vec response; _hidl_cb(response); return Void(); } Return SecureElement::isCardPresent() { return true; } Return SecureElement::transmit(const hidl_vec& data, transmit_cb _hidl_cb) { ESESTATUS status = ESESTATUS_FAILED; phNxpEse_data cmdApdu; phNxpEse_data rspApdu; phNxpEse_memset(&cmdApdu, 0x00, sizeof(phNxpEse_data)); phNxpEse_memset(&rspApdu, 0x00, sizeof(phNxpEse_data)); cmdApdu.len = data.size(); if (cmdApdu.len >= MIN_APDU_LENGTH) { cmdApdu.p_data = (uint8_t*)phNxpEse_memalloc(data.size() * sizeof(uint8_t)); memcpy(cmdApdu.p_data, data.data(), cmdApdu.len); status = phNxpEse_Transceive(&cmdApdu, &rspApdu); } hidl_vec result; if (status != ESESTATUS_SUCCESS) { ALOGE("%s: transmit failed!!!", __func__); } else { result.resize(rspApdu.len); memcpy(&result[0], rspApdu.p_data, rspApdu.len); } _hidl_cb(result); phNxpEse_free(cmdApdu.p_data); phNxpEse_free(rspApdu.p_data); return Void(); } Return SecureElement::openLogicalChannel(const hidl_vec& aid, uint8_t p2, openLogicalChannel_cb _hidl_cb) { hidl_vec manageChannelCommand = {0x00, 0x70, 0x00, 0x00, 0x01}; LogicalChannelResponse resApduBuff; resApduBuff.channelNumber = 0xff; memset(&resApduBuff, 0x00, sizeof(resApduBuff)); if (!isSeInitialized()) { ESESTATUS status = seHalInit(); if (status != ESESTATUS_SUCCESS) { ALOGE("%s: seHalInit Failed!!!", __func__); _hidl_cb(resApduBuff, SecureElementStatus::IOERROR); return Void(); } } SecureElementStatus sestatus = SecureElementStatus::IOERROR; ESESTATUS status = ESESTATUS_FAILED; phNxpEse_data cmdApdu; phNxpEse_data rspApdu; phNxpEse_memset(&cmdApdu, 0x00, sizeof(phNxpEse_data)); phNxpEse_memset(&rspApdu, 0x00, sizeof(phNxpEse_data)); cmdApdu.len = manageChannelCommand.size(); cmdApdu.p_data = (uint8_t*)phNxpEse_memalloc(manageChannelCommand.size() * sizeof(uint8_t)); if (cmdApdu.p_data != NULL) { memcpy(cmdApdu.p_data, manageChannelCommand.data(), cmdApdu.len); status = phNxpEse_Transceive(&cmdApdu, &rspApdu); } if (status != ESESTATUS_SUCCESS) { /*Transceive failed*/ sestatus = SecureElementStatus::IOERROR; } else if (rspApdu.p_data[rspApdu.len - 2] == 0x90 && rspApdu.p_data[rspApdu.len - 1] == 0x00) { /*ManageChannel successful*/ resApduBuff.channelNumber = rspApdu.p_data[0]; mOpenedchannelCount++; mOpenedChannels[resApduBuff.channelNumber] = true; sestatus = SecureElementStatus::SUCCESS; } else if (rspApdu.p_data[rspApdu.len - 2] == 0x6A && rspApdu.p_data[rspApdu.len - 1] == 0x81) { sestatus = SecureElementStatus::CHANNEL_NOT_AVAILABLE; } else if (((rspApdu.p_data[rspApdu.len - 2] == 0x6E) || (rspApdu.p_data[rspApdu.len - 2] == 0x6D)) && rspApdu.p_data[rspApdu.len - 1] == 0x00) { sestatus = SecureElementStatus::UNSUPPORTED_OPERATION; } /*Free the allocations*/ phNxpEse_free(cmdApdu.p_data); phNxpEse_free(rspApdu.p_data); if (sestatus != SecureElementStatus::SUCCESS) { /*If first logical channel open fails, DeInit SE*/ if (isSeInitialized() && (mOpenedchannelCount == 0)) { SecureElementStatus deInitStatus = seHalDeInit(); if (deInitStatus != SecureElementStatus::SUCCESS) { ALOGE("%s: seDeInit Failed", __func__); } } /*If manageChanle is failed in any of above cases send the callback and return*/ _hidl_cb(resApduBuff, sestatus); return Void(); } ALOGD_IF(ese_debug_enabled, "%s: Sending selectApdu", __func__); /*Reset variables if manageChannel is success*/ sestatus = SecureElementStatus::IOERROR; status = ESESTATUS_FAILED; phNxpEse_memset(&cmdApdu, 0x00, sizeof(phNxpEse_data)); phNxpEse_memset(&rspApdu, 0x00, sizeof(phNxpEse_data)); cmdApdu.len = (int32_t)(5 + aid.size()); cmdApdu.p_data = (uint8_t*)phNxpEse_memalloc(cmdApdu.len * sizeof(uint8_t)); if (cmdApdu.p_data != NULL) { uint8_t xx = 0; cmdApdu.p_data[xx++] = resApduBuff.channelNumber; cmdApdu.p_data[xx++] = 0xA4; // INS cmdApdu.p_data[xx++] = 0x04; // P1 cmdApdu.p_data[xx++] = p2; // P2 cmdApdu.p_data[xx++] = aid.size(); // Lc memcpy(&cmdApdu.p_data[xx], aid.data(), aid.size()); status = phNxpEse_Transceive(&cmdApdu, &rspApdu); } if (status != ESESTATUS_SUCCESS) { /*Transceive failed*/ sestatus = SecureElementStatus::IOERROR; } else { uint8_t sw1 = rspApdu.p_data[rspApdu.len - 2]; uint8_t sw2 = rspApdu.p_data[rspApdu.len - 1]; /*Return response on success, empty vector on failure*/ /*Status is success*/ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x62) || (sw1 == 0x63)) { /*Copy the response including status word*/ resApduBuff.selectResponse.resize(rspApdu.len); memcpy(&resApduBuff.selectResponse[0], rspApdu.p_data, rspApdu.len); sestatus = SecureElementStatus::SUCCESS; } /*AID provided doesn't match any applet on the secure element*/ else if ((sw1 == 0x6A && sw2 == 0x82) || (sw1 == 0x69 && (sw2 == 0x99 || sw2 == 0x85))) { sestatus = SecureElementStatus::NO_SUCH_ELEMENT_ERROR; } /*Operation provided by the P2 parameter is not permitted by the applet.*/ else if (sw1 == 0x6A && sw2 == 0x86) { sestatus = SecureElementStatus::UNSUPPORTED_OPERATION; } } if (sestatus != SecureElementStatus::SUCCESS) { SecureElementStatus closeChannelStatus = closeChannel(resApduBuff.channelNumber); if (closeChannelStatus != SecureElementStatus::SUCCESS) { ALOGE("%s: closeChannel Failed", __func__); } else { resApduBuff.channelNumber = 0xff; } } _hidl_cb(resApduBuff, sestatus); phNxpEse_free(cmdApdu.p_data); phNxpEse_free(rspApdu.p_data); return Void(); } Return SecureElement::openBasicChannel(const hidl_vec& aid, uint8_t p2, openBasicChannel_cb _hidl_cb) { hidl_vec result; if (!isSeInitialized()) { ESESTATUS status = seHalInit(); if (status != ESESTATUS_SUCCESS) { ALOGE("%s: seHalInit Failed!!!", __func__); _hidl_cb(result, SecureElementStatus::IOERROR); return Void(); } } SecureElementStatus sestatus = SecureElementStatus::IOERROR; ESESTATUS status = ESESTATUS_FAILED; phNxpEse_data cmdApdu; phNxpEse_data rspApdu; phNxpEse_memset(&cmdApdu, 0x00, sizeof(phNxpEse_data)); phNxpEse_memset(&rspApdu, 0x00, sizeof(phNxpEse_data)); cmdApdu.len = (int32_t)(5 + aid.size()); cmdApdu.p_data = (uint8_t*)phNxpEse_memalloc(cmdApdu.len * sizeof(uint8_t)); if (cmdApdu.p_data != NULL) { uint8_t xx = 0; cmdApdu.p_data[xx++] = 0x00; // basic channel cmdApdu.p_data[xx++] = 0xA4; // INS cmdApdu.p_data[xx++] = 0x04; // P1 cmdApdu.p_data[xx++] = p2; // P2 cmdApdu.p_data[xx++] = aid.size(); // Lc memcpy(&cmdApdu.p_data[xx], aid.data(), aid.size()); status = phNxpEse_Transceive(&cmdApdu, &rspApdu); } if (status != ESESTATUS_SUCCESS) { /* Transceive failed */ sestatus = SecureElementStatus::IOERROR; } else { uint8_t sw1 = rspApdu.p_data[rspApdu.len - 2]; uint8_t sw2 = rspApdu.p_data[rspApdu.len - 1]; /*Return response on success, empty vector on failure*/ /*Status is success*/ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x62) || (sw1 == 0x63)) { /*Copy the response including status word*/ result.resize(rspApdu.len); memcpy(&result[0], rspApdu.p_data, rspApdu.len); /*Set basic channel reference if it is not set */ if (!mOpenedChannels[0]) { mOpenedChannels[0] = true; mOpenedchannelCount++; } sestatus = SecureElementStatus::SUCCESS; } /*AID provided doesn't match any applet on the secure element*/ else if ((sw1 == 0x6A && sw2 == 0x82) || (sw1 == 0x69 && (sw2 == 0x99 || sw2 == 0x85))) { sestatus = SecureElementStatus::NO_SUCH_ELEMENT_ERROR; } /*Operation provided by the P2 parameter is not permitted by the applet.*/ else if (sw1 == 0x6A && sw2 == 0x86) { sestatus = SecureElementStatus::UNSUPPORTED_OPERATION; } } if (sestatus != SecureElementStatus::SUCCESS) { SecureElementStatus closeStatus = SecureElementStatus::IOERROR; /*If first basic channel open fails, DeInit SE*/ if ((mOpenedChannels[DEFAULT_BASIC_CHANNEL] == false) && (mOpenedchannelCount == 0)) { closeStatus = seHalDeInit(); } else { closeStatus = closeChannel(DEFAULT_BASIC_CHANNEL); } if (closeStatus != SecureElementStatus::SUCCESS) { ALOGE("%s: close Failed", __func__); } } _hidl_cb(result, sestatus); phNxpEse_free(cmdApdu.p_data); phNxpEse_free(rspApdu.p_data); return Void(); } Return<::android::hardware::secure_element::V1_0::SecureElementStatus> SecureElement::closeChannel(uint8_t channelNumber) { ESESTATUS status = ESESTATUS_FAILED; SecureElementStatus sestatus = SecureElementStatus::FAILED; phNxpEse_data cmdApdu; phNxpEse_data rspApdu; if ((channelNumber >= MAX_LOGICAL_CHANNELS) || (mOpenedChannels[channelNumber] == false)) { ALOGE("%s: invalid channel!!!", __func__); sestatus = SecureElementStatus::FAILED; } else if (channelNumber > DEFAULT_BASIC_CHANNEL) { phNxpEse_memset(&cmdApdu, 0x00, sizeof(phNxpEse_data)); phNxpEse_memset(&rspApdu, 0x00, sizeof(phNxpEse_data)); cmdApdu.p_data = (uint8_t*)phNxpEse_memalloc(5 * sizeof(uint8_t)); if (cmdApdu.p_data != NULL) { uint8_t xx = 0; cmdApdu.p_data[xx++] = channelNumber; cmdApdu.p_data[xx++] = 0x70; // INS cmdApdu.p_data[xx++] = 0x80; // P1 cmdApdu.p_data[xx++] = channelNumber; // P2 cmdApdu.p_data[xx++] = 0x00; // Lc cmdApdu.len = xx; status = phNxpEse_Transceive(&cmdApdu, &rspApdu); } if (status != ESESTATUS_SUCCESS) { sestatus = SecureElementStatus::FAILED; } else if ((rspApdu.p_data[rspApdu.len - 2] == 0x90) && (rspApdu.p_data[rspApdu.len - 1] == 0x00)) { sestatus = SecureElementStatus::SUCCESS; } else { sestatus = SecureElementStatus::FAILED; } phNxpEse_free(cmdApdu.p_data); phNxpEse_free(rspApdu.p_data); } if (mOpenedChannels[channelNumber] != false) mOpenedchannelCount--; mOpenedChannels[channelNumber] = false; /*If there are no channels remaining close secureElement*/ if (mOpenedchannelCount == 0) { sestatus = seHalDeInit(); } else { sestatus = SecureElementStatus::SUCCESS; } return sestatus; } void SecureElement::serviceDied(uint64_t /*cookie*/, const wp& /*who*/) { ALOGE("%s: SecureElement serviceDied!!!", __func__); SecureElementStatus sestatus = seHalDeInit(); if (sestatus != SecureElementStatus::SUCCESS) { ALOGE("%s: seHalDeInit Faliled!!!", __func__); } if (mCallbackV1_0 != nullptr) { mCallbackV1_0->unlinkToDeath(this); } } bool SecureElement::isSeInitialized() { return phNxpEse_isOpen(); } ESESTATUS SecureElement::seHalInit() { ESESTATUS status = ESESTATUS_SUCCESS; phNxpEse_initParams initParams; memset(&initParams, 0x00, sizeof(phNxpEse_initParams)); initParams.initMode = ESE_MODE_NORMAL; status = phNxpEse_open(initParams); if (status != ESESTATUS_SUCCESS) { ALOGE("%s: SecureElement open failed!!!", __func__); } else { status = phNxpEse_init(initParams); if (status != ESESTATUS_SUCCESS) { ALOGE("%s: SecureElement init failed!!!", __func__); } } return status; } Return<::android::hardware::secure_element::V1_0::SecureElementStatus> SecureElement::seHalDeInit() { ESESTATUS status = ESESTATUS_SUCCESS; SecureElementStatus sestatus = SecureElementStatus::FAILED; status = phNxpEse_deInit(); if (status != ESESTATUS_SUCCESS) { sestatus = SecureElementStatus::FAILED; } else { status = phNxpEse_close(); if (status != ESESTATUS_SUCCESS) { sestatus = SecureElementStatus::FAILED; } else { sestatus = SecureElementStatus::SUCCESS; for (uint8_t xx = 0; xx < MAX_LOGICAL_CHANNELS; xx++) { mOpenedChannels[xx] = false; } mOpenedchannelCount = 0; } } return sestatus; } void SecureElement::onStateChange(bool result, std::string reason) { ALOGD("%s: result: %d, reaon= %s", __func__, result, reason.c_str()); mCallbackV1_0->onStateChange(result); } } // namespace implementation } // namespace V1_0 } // namespace secure_element } // namespace hardware } // namespace android