14#include "keyple/core/service/LocalReaderAdapter.hpp"
23#include "keyple/core/plugin/CardIOException.hpp"
24#include "keyple/core/plugin/ReaderIOException.hpp"
25#include "keyple/core/plugin/spi/reader/AutonomousSelectionReaderSpi.hpp"
26#include "keyple/core/plugin/spi/reader/ConfigurableReaderSpi.hpp"
27#include "keyple/core/service/CardSelectionResponseAdapter.hpp"
28#include "keyple/core/util/ApduUtil.hpp"
29#include "keyple/core/util/HexUtil.hpp"
30#include "keyple/core/util/KeypleAssert.hpp"
31#include "keyple/core/util/cpp/Arrays.hpp"
32#include "keyple/core/util/cpp/KeypleStd.hpp"
33#include "keyple/core/util/cpp/System.hpp"
34#include "keyple/core/util/cpp/exception/Exception.hpp"
35#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
36#include "keyple/core/util/cpp/exception/RuntimeException.hpp"
37#include "keypop/card/CardBrokenCommunicationException.hpp"
38#include "keypop/card/ReaderBrokenCommunicationException.hpp"
39#include "keypop/card/UnexpectedStatusWordException.hpp"
40#include "keypop/reader/ReaderCommunicationException.hpp"
41#include "keypop/reader/ReaderProtocolNotSupportedException.hpp"
47using keyple::core::plugin::CardIOException;
48using keyple::core::plugin::ReaderIOException;
49using keyple::core::plugin::spi::reader::AutonomousSelectionReaderSpi;
50using keyple::core::plugin::spi::reader::ConfigurableReaderSpi;
51using keyple::core::util::ApduUtil;
52using keyple::core::util::Assert;
53using keyple::core::util::HexUtil;
54using keyple::core::util::cpp::Arrays;
55using keyple::core::util::cpp::System;
56using keyple::core::util::cpp::exception::Exception;
57using keyple::core::util::cpp::exception::IllegalStateException;
58using keyple::core::util::cpp::exception::RuntimeException;
59using keypop::card::CardBrokenCommunicationException;
60using keypop::card::ReaderBrokenCommunicationException;
61using keypop::card::UnexpectedStatusWordException;
62using keypop::reader::ReaderCommunicationException;
63using keypop::reader::ReaderProtocolNotSupportedException;
68const int LocalReaderAdapter::SW_6100 = 0x6100;
70const int LocalReaderAdapter::SW_6C00 = 0x6C00;
72const int LocalReaderAdapter::SW1_MASK = 0xFF00;
73const int LocalReaderAdapter::SW2_MASK = 0x00FF;
75LocalReaderAdapter::LocalReaderAdapter(
76 std::shared_ptr<ReaderSpi> readerSpi,
const std::string& pluginName)
77: AbstractReaderAdapter(
79 std::dynamic_pointer_cast<KeypleReaderExtension>(readerSpi),
81, mReaderSpi(readerSpi)
83, mIsLogicalChannelOpen(false)
84, mUseDefaultProtocol(false)
85, mCurrentLogicalProtocolName(
"")
86, mCurrentPhysicalProtocolName(
"")
87, mProtocolAssociations({})
92LocalReaderAdapter::computeCurrentProtocol()
94 mCurrentLogicalProtocolName =
"";
95 mCurrentPhysicalProtocolName =
"";
97 if (mProtocolAssociations.empty()) {
98 mUseDefaultProtocol =
true;
100 mUseDefaultProtocol =
false;
102 const auto configurable
103 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
104 for (
const auto& entry : mProtocolAssociations) {
105 if (configurable->isCurrentProtocol(entry.first)) {
106 mCurrentLogicalProtocolName = entry.second;
107 mCurrentPhysicalProtocolName = entry.first;
114LocalReaderAdapter::getCurrentPhysicalProtocolName()
const
116 return mCurrentPhysicalProtocolName;
120LocalReaderAdapter::closeLogicalChannel()
122 mLogger->trace(
"Reader [%] closes logical channel\n", getName());
125 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
129 reader->closeLogicalChannel();
132 mIsLogicalChannelOpen =
false;
133 mLogger->trace(
"Logical channel closed\n");
137LocalReaderAdapter::computeSelectApplicationP2(
138 const FileOccurrence fileOccurrence,
139 const FileControlInformation fileControlInformation)
143 switch (fileOccurrence) {
144 case FileOccurrence::FIRST:
147 case FileOccurrence::LAST:
150 case FileOccurrence::NEXT:
153 case FileOccurrence::PREVIOUS:
157 std::stringstream ss;
158 ss << fileOccurrence;
159 throw IllegalStateException(
"Unexpected value: " + ss.str());
162 switch (fileControlInformation) {
163 case FileControlInformation::FCI:
166 case FileControlInformation::FCP:
169 case FileControlInformation::FMD:
172 case FileControlInformation::NO_RESPONSE:
176 std::stringstream ss;
177 ss << fileControlInformation;
178 throw IllegalStateException(
"Unexpected value: " + ss.str());
184std::shared_ptr<ApduResponseAdapter>
185LocalReaderAdapter::processExplicitAidSelection(
186 std::shared_ptr<InternalIsoCardSelector> cardSelector)
188 const std::vector<uint8_t>& aid = cardSelector->getAid();
191 "Reader [%] selects application with AID [%]\n",
193 HexUtil::toHex(aid));
202 std::vector<uint8_t> selectApplicationCommand(6 + aid.size());
203 selectApplicationCommand[0] = 0x00;
204 selectApplicationCommand[1] = 0xA4;
205 selectApplicationCommand[2] = 0x04;
210 selectApplicationCommand[3] = computeSelectApplicationP2(
211 cardSelector->getFileOccurrence(),
212 cardSelector->getFileControlInformation());
213 selectApplicationCommand[4] =
static_cast<uint8_t
>(aid.size());
215 aid, 0, selectApplicationCommand, 5,
static_cast<int>(aid.size()));
216 selectApplicationCommand[5 + aid.size()] = 0x00;
218 auto apduRequest = std::shared_ptr<ApduRequest>(
219 new ApduRequest(selectApplicationCommand));
220 apduRequest->setInfo(
"Internal Select Application");
222 return processApduRequest(apduRequest);
225std::shared_ptr<ApduResponseAdapter>
226LocalReaderAdapter::selectByAid(
227 std::shared_ptr<InternalIsoCardSelector> cardSelector)
229 std::shared_ptr<ApduResponseAdapter> fciResponse =
nullptr;
235 Assert::getInstance().isInRange(
236 cardSelector->getAid().size(), 0, 16,
"aid");
239 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
241 const std::vector<uint8_t>& aid = cardSelector->getAid();
242 const uint8_t p2 = computeSelectApplicationP2(
243 cardSelector->getFileOccurrence(),
244 cardSelector->getFileControlInformation());
245 const std::vector<uint8_t> selectionDataBytes
246 = reader->openChannelForAid(aid, p2);
247 fciResponse = std::make_shared<ApduResponseAdapter>(selectionDataBytes);
249 fciResponse = processExplicitAidSelection(cardSelector);
256LocalReaderAdapter::checkPowerOnData(
257 const std::string& powerOnData,
258 std::shared_ptr<InternalCardSelector> cardSelector)
260 const std::string& powerOnDataRegex = cardSelector->getPowerOnDataRegex();
263 if (powerOnData !=
"" && powerOnDataRegex !=
""
264 && !std::regex_match(powerOnData, std::regex(powerOnDataRegex))) {
266 "Power-on data didn't match (powerOnData: %, powerOnDataRegex: %\n",
278std::shared_ptr<LocalReaderAdapter::SelectionStatus>
279LocalReaderAdapter::processSelection(
280 std::shared_ptr<CardSelectorBase> cardSelector,
281 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest)
285 std::string powerOnData =
"";
286 std::shared_ptr<ApduResponseAdapter> fciResponse =
nullptr;
287 bool hasMatched =
true;
289 auto internalSelector
290 = std::dynamic_pointer_cast<InternalCardSelector>(cardSelector);
291 if (!internalSelector) {
292 throw RuntimeException(
293 "cardSelector is not of type InternalCardSelector.");
296 const std::string& logicalProtocolName
297 = internalSelector->getLogicalProtocolName();
298 if (logicalProtocolName !=
"" && mUseDefaultProtocol) {
299 throw IllegalStateException(
300 "Protocol " + logicalProtocolName
301 +
" not associated to a reader protocol.");
305 if (logicalProtocolName ==
""
306 || logicalProtocolName == mCurrentLogicalProtocolName) {
312 powerOnData = mReaderSpi->getPowerOnData();
313 if (checkPowerOnData(powerOnData, internalSelector)) {
316 const auto internalIsoCardSelector
317 = std::dynamic_pointer_cast<InternalIsoCardSelector>(
319 if (internalIsoCardSelector
320 && internalIsoCardSelector->getAid().size() != 0) {
321 fciResponse = selectByAid(internalIsoCardSelector);
322 const std::vector<int>& statusWords
323 = cardSelectionRequest
324 ->getSuccessfulSelectionStatusWords();
325 hasMatched = std::find(
328 fciResponse->getStatusWord())
329 != statusWords.end();
331 fciResponse =
nullptr;
336 fciResponse =
nullptr;
341 fciResponse =
nullptr;
345 return std::make_shared<SelectionStatus>(
346 powerOnData, fciResponse, hasMatched);
348 }
catch (
const ReaderIOException& e) {
349 throw ReaderBrokenCommunicationException(
350 std::make_shared<CardResponseAdapter>(
351 std::vector<std::shared_ptr<ApduResponseApi>>({}),
false),
354 std::make_shared<ReaderIOException>(e));
356 }
catch (
const CardIOException& e) {
357 throw CardBrokenCommunicationException(
358 std::make_shared<CardResponseAdapter>(
359 std::vector<std::shared_ptr<ApduResponseApi>>({}),
false),
362 std::make_shared<CardIOException>(e));
366std::shared_ptr<CardSelectionResponseApi>
367LocalReaderAdapter::processCardSelectionRequest(
368 std::shared_ptr<CardSelectorBase> cardSelector,
369 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest,
370 const ChannelControl channelControl)
372 mIsLogicalChannelOpen =
false;
374 std::shared_ptr<SelectionStatus> selectionStatus(
375 processSelection(cardSelector, cardSelectionRequest));
376 if (!selectionStatus->mHasMatched) {
379 return std::make_shared<CardSelectionResponseAdapter>(
380 selectionStatus->mPowerOnData,
381 selectionStatus->mSelectApplicationResponse,
383 std::make_shared<CardResponseAdapter>(
384 std::vector<std::shared_ptr<ApduResponseApi>>({}),
false));
387 mIsLogicalChannelOpen =
true;
389 std::shared_ptr<CardResponseAdapter> cardResponse =
nullptr;
391 if (cardSelectionRequest->getCardRequest() !=
nullptr) {
393 = std::dynamic_pointer_cast<CardResponseAdapter>(processCardRequest(
394 cardSelectionRequest->getCardRequest(), channelControl));
396 cardResponse =
nullptr;
399 return std::make_shared<CardSelectionResponseAdapter>(
400 selectionStatus->mPowerOnData,
401 selectionStatus->mSelectApplicationResponse,
406std::shared_ptr<ApduResponseAdapter>
407LocalReaderAdapter::processApduRequest(
408 const std::shared_ptr<ApduRequestSpi> apduRequest)
410 std::shared_ptr<ApduResponseAdapter> apduResponse =
nullptr;
412 uint64_t timeStamp = System::nanoTime();
413 uint64_t elapsed10ms = (timeStamp - mBefore) / 100000;
417 "Reader [%] --> apduRequest: %, elapsed % ms\n",
422 apduResponse = std::make_shared<ApduResponseAdapter>(
423 mReaderSpi->transmitApdu(apduRequest->getApdu()));
425 timeStamp = System::nanoTime();
426 elapsed10ms = (timeStamp - mBefore) / 100000;
430 "Reader [%] <-- apduResponse: %, elapsed % ms\n",
435 if (apduResponse->getDataOut().size() == 0) {
436 if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6100) {
441 const uint8_t le = apduResponse->getStatusWord() & SW2_MASK;
442 const std::vector<uint8_t> getResponseApdu
443 = {0x00, 0xC0, 0x00, 0x00, le};
446 auto adapter = std::shared_ptr<ApduRequest>(
447 new ApduRequest(getResponseApdu));
448 adapter->setInfo(
"Internal Get Response");
449 apduResponse = processApduRequest(adapter);
451 }
else if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6C00) {
456 std::vector<std::uint8_t> apdu = apduRequest->getApdu();
457 apdu[apduRequest->getApdu().size() - 1]
458 = (apduResponse->getStatusWord() & SW2_MASK);
459 apduRequest->setApdu(apdu);
462 apduResponse = processApduRequest(apduRequest);
465 ApduUtil::isCase4(apduRequest->getApdu())
467 apduRequest->getSuccessfulStatusWords(),
468 apduResponse->getStatusWord())) {
475 = apduRequest->getApdu()[apduRequest->getApdu().size() - 1];
476 const std::vector<uint8_t> getResponseApdu
477 = {0x00, 0xC0, 0x00, 0x00, le};
480 auto adapter = std::shared_ptr<ApduRequest>(
481 new ApduRequest(getResponseApdu));
482 adapter->setInfo(
"Internal Get Response");
483 apduResponse = processApduRequest(adapter);
491LocalReaderAdapter::releaseChannel()
496 mReaderSpi->closePhysicalChannel();
497 }
catch (
const ReaderIOException& e) {
498 throw ReaderBrokenCommunicationException(
501 "Failed to release the physical channel",
502 std::make_shared<ReaderIOException>(e));
507LocalReaderAdapter::deactivateReaderProtocol(
const std::string& readerProtocol)
511 Assert::getInstance().notEmpty(readerProtocol,
"readerProtocol");
513 mProtocolAssociations.erase(readerProtocol);
515 const auto configurable
516 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
517 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
518 throw ReaderProtocolNotSupportedException(readerProtocol);
521 configurable->deactivateProtocol(readerProtocol);
525LocalReaderAdapter::activateReaderProtocol(
526 const std::string& readerProtocol,
const std::string& applicationProtocol)
530 Assert::getInstance()
531 .notEmpty(readerProtocol,
"readerProtocol")
532 .notEmpty(applicationProtocol,
"applicationProtocol");
534 const auto configurable
535 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
536 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
537 throw ReaderProtocolNotSupportedException(readerProtocol);
540 configurable->activateProtocol(readerProtocol);
542 mProtocolAssociations.insert({readerProtocol, applicationProtocol});
546LocalReaderAdapter::isCardPresent()
555 return mReaderSpi->checkCardPresence();
556 }
catch (
const ReaderIOException& e) {
557 throw ReaderCommunicationException(
558 "An exception occurred while checking the card presence", e);
563LocalReaderAdapter::isContactless()
565 return mReaderSpi->isContactless();
568std::shared_ptr<CardResponseApi>
569LocalReaderAdapter::processCardRequest(
570 const std::shared_ptr<CardRequestSpi> cardRequest,
571 const ChannelControl channelControl)
576 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses;
578 for (
const auto& apduRequest : cardRequest->getApduRequests()) {
580 const std::shared_ptr<ApduResponseAdapter> apduResponse(
581 processApduRequest(apduRequest));
582 apduResponses.push_back(apduResponse);
583 if (cardRequest->stopOnUnsuccessfulStatusWord()
584 && !Arrays::contains(
585 apduRequest->getSuccessfulStatusWords(),
586 apduResponse->getStatusWord())) {
587 if (channelControl == ChannelControl::CLOSE_AFTER) {
588 closeLogicalAndPhysicalChannelsSilently();
590 throw UnexpectedStatusWordException(
591 std::make_shared<CardResponseAdapter>(apduResponses,
false),
592 cardRequest->getApduRequests().size()
593 == apduResponses.size(),
594 "Unexpected status word");
597 }
catch (
const ReaderIOException& e) {
598 closeLogicalAndPhysicalChannelsSilently();
599 throw ReaderBrokenCommunicationException(
600 std::make_shared<CardResponseAdapter>(apduResponses,
false),
602 "Reader communication failure while transmitting a card "
604 std::make_shared<ReaderIOException>(e));
606 }
catch (
const CardIOException& e) {
607 closeLogicalAndPhysicalChannelsSilently();
608 throw CardBrokenCommunicationException(
609 std::make_shared<CardResponseAdapter>(apduResponses,
false),
611 "Card communication failure while transmitting a card request",
612 std::make_shared<CardIOException>(e));
617 if (channelControl == ChannelControl::CLOSE_AFTER) {
621 return std::make_shared<CardResponseAdapter>(
622 apduResponses, mIsLogicalChannelOpen);
625std::vector<std::shared_ptr<CardSelectionResponseApi>>
626LocalReaderAdapter::processCardSelectionRequests(
627 const std::vector<std::shared_ptr<CardSelectorBase>>& cardSelectors,
628 const std::vector<std::shared_ptr<CardSelectionRequestSpi>>&
629 cardSelectionRequests,
630 const MultiSelectionProcessing multiSelectionProcessing,
631 const ChannelControl channelControl)
636 if (!mReaderSpi->isPhysicalChannelOpen()) {
638 mReaderSpi->openPhysicalChannel();
639 computeCurrentProtocol();
640 }
catch (
const ReaderIOException& e) {
641 throw ReaderBrokenCommunicationException(
644 "Reader communication failure while opening physical channel",
645 std::make_shared<ReaderIOException>(e));
646 }
catch (
const CardIOException& e) {
647 throw CardBrokenCommunicationException(
650 "Card communication failure while opening physical channel",
651 std::make_shared<CardIOException>(e));
655 std::vector<std::shared_ptr<CardSelectionResponseApi>>
656 cardSelectionResponses;
660 = std::make_pair(cardSelectors.begin(), cardSelectionRequests.begin());
661 p.second != cardSelectionRequests.end()
662 && p.first != cardSelectors.end();
663 ++p.first, ++p.second) {
665 const auto cardSelectionResponse
666 = processCardSelectionRequest(*p.first, *p.second, channelControl);
667 cardSelectionResponses.push_back(cardSelectionResponse);
669 if (multiSelectionProcessing == MultiSelectionProcessing::PROCESS_ALL) {
674 closeLogicalChannel();
676 if (mIsLogicalChannelOpen) {
684 if (channelControl == ChannelControl::CLOSE_AFTER) {
688 return cardSelectionResponses;
692LocalReaderAdapter::doUnregister()
695 mReaderSpi->closePhysicalChannel();
696 }
catch (
const Exception& e) {
698 "Error closing physical channel on reader [%] - %\n", getName(), e);
702 mReaderSpi->onUnregister();
703 }
catch (
const Exception& e) {
705 "Error unregistering reader extension of reader [%]: % - %\n",
711 AbstractReaderAdapter::doUnregister();
715LocalReaderAdapter::closeLogicalAndPhysicalChannelsSilently()
717 closeLogicalChannel();
720 mCurrentLogicalProtocolName =
"";
721 mUseDefaultProtocol =
false;
724 mReaderSpi->closePhysicalChannel();
725 }
catch (
const ReaderIOException& e) {
727 "Error closing physical channel on reader [%]: % - %\n",
735LocalReaderAdapter::isLogicalChannelOpen()
const
737 return mIsLogicalChannelOpen;
740std::shared_ptr<ReaderSpi>
741LocalReaderAdapter::getReaderSpi()
const
750LocalReaderAdapter::SelectionStatus::SelectionStatus(
751 const std::string& powerOnData,
752 const std::shared_ptr<ApduResponseAdapter> selectApplicationResponse,
753 const bool hasMatched)
754: mPowerOnData(powerOnData)
755, mSelectApplicationResponse(selectApplicationResponse)
756, mHasMatched(hasMatched)
764const int LocalReaderAdapter::ApduRequest::DEFAULT_SUCCESSFUL_CODE = 0x9000;
766LocalReaderAdapter::ApduRequest::ApduRequest(
const std::vector<uint8_t>& apdu)
768, mSuccessfulStatusWords({DEFAULT_SUCCESSFUL_CODE})
772LocalReaderAdapter::ApduRequest&
773LocalReaderAdapter::ApduRequest::setInfo(
const std::string& info)
780const std::vector<uint8_t>&
781LocalReaderAdapter::ApduRequest::getApdu()
const
787LocalReaderAdapter::ApduRequest::setApdu(
const std::vector<std::uint8_t>& apdu)
792const std::vector<int>&
793LocalReaderAdapter::ApduRequest::getSuccessfulStatusWords()
const
795 return mSuccessfulStatusWords;
799LocalReaderAdapter::ApduRequest::getInfo()
const
805operator<<(std::ostream& os, LocalReaderAdapter::ApduRequest& ar)
807 os <<
"APDU_REQUEST: {"
808 <<
"APDU: " << ar.getApdu() <<
", "
809 <<
"SUCCESSFUL_STATUS_WORDS: " << ar.getSuccessfulStatusWords() <<
", "
810 <<
"INFO: " << ar.getInfo() <<
"}";
817 std::ostream& os,
const std::shared_ptr<LocalReaderAdapter::ApduRequest> ar)
820 os <<
"APDU_REQUEST: null";
831 const std::vector<std::shared_ptr<LocalReaderAdapter::ApduRequest>>& ars)
833 os <<
"APDU_REQUESTS: {";
835 for (
auto it = ars.begin(); it != ars.end(); it++) {
836 if (it != ars.begin()) {
std::ostream & operator<<(std::ostream &os, const std::vector< std::shared_ptr< LocalReaderAdapter::ApduRequest > > &ars)