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/service/SmartCardServiceProvider.hpp"
29#include "keyple/core/util/ApduUtil.hpp"
30#include "keyple/core/util/HexUtil.hpp"
31#include "keyple/core/util/KeypleAssert.hpp"
32#include "keyple/core/util/cpp/Arrays.hpp"
33#include "keyple/core/util/cpp/KeypleStd.hpp"
34#include "keyple/core/util/cpp/System.hpp"
35#include "keyple/core/util/cpp/exception/Exception.hpp"
36#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
37#include "keyple/core/util/cpp/exception/RuntimeException.hpp"
38#include "keypop/card/CardBrokenCommunicationException.hpp"
39#include "keypop/card/ReaderBrokenCommunicationException.hpp"
40#include "keypop/card/UnexpectedStatusWordException.hpp"
41#include "keypop/reader/ReaderCommunicationException.hpp"
42#include "keypop/reader/ReaderProtocolNotSupportedException.hpp"
48using keyple::core::plugin::CardIOException;
49using keyple::core::plugin::ReaderIOException;
50using keyple::core::plugin::spi::reader::AutonomousSelectionReaderSpi;
51using keyple::core::plugin::spi::reader::ConfigurableReaderSpi;
52using keyple::core::util::ApduUtil;
53using keyple::core::util::Assert;
54using keyple::core::util::HexUtil;
55using keyple::core::util::cpp::Arrays;
56using keyple::core::util::cpp::System;
57using keyple::core::util::cpp::exception::Exception;
58using keyple::core::util::cpp::exception::IllegalStateException;
59using keyple::core::util::cpp::exception::RuntimeException;
60using keypop::card::CardBrokenCommunicationException;
61using keypop::card::ReaderBrokenCommunicationException;
62using keypop::card::UnexpectedStatusWordException;
63using keypop::reader::ReaderCommunicationException;
64using keypop::reader::ReaderProtocolNotSupportedException;
69const int LocalReaderAdapter::SW_6100 = 0x6100;
71const int LocalReaderAdapter::SW_6C00 = 0x6C00;
73const int LocalReaderAdapter::SW1_MASK = 0xFF00;
74const int LocalReaderAdapter::SW2_MASK = 0x00FF;
76LocalReaderAdapter::LocalReaderAdapter(
77 std::shared_ptr<ReaderSpi> readerSpi,
const std::string& pluginName)
78: AbstractReaderAdapter(
80 std::dynamic_pointer_cast<KeypleReaderExtension>(readerSpi),
82, mReaderSpi(readerSpi)
84, mIsLogicalChannelOpen(false)
85, mUseDefaultProtocol(false)
86, mCurrentLogicalProtocolName(
"")
87, mCurrentPhysicalProtocolName(
"")
88, mProtocolAssociations({})
89, mIsAutomaticStatusCodeHandlingEnabled(
90 SmartCardServiceProvider::getService()
91 ->isAutomaticStatusCodeHandlingEnabled())
96LocalReaderAdapter::computeCurrentProtocol()
98 mCurrentLogicalProtocolName =
"";
99 mCurrentPhysicalProtocolName =
"";
101 if (mProtocolAssociations.empty()) {
102 mUseDefaultProtocol =
true;
104 mUseDefaultProtocol =
false;
106 const auto configurable
107 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
108 for (
const auto& entry : mProtocolAssociations) {
109 if (configurable->isCurrentProtocol(entry.first)) {
110 mCurrentLogicalProtocolName = entry.second;
111 mCurrentPhysicalProtocolName = entry.first;
118LocalReaderAdapter::getCurrentPhysicalProtocolName()
const
120 return mCurrentPhysicalProtocolName;
124LocalReaderAdapter::closeLogicalChannel()
126 mLogger->trace(
"Reader [%] closes logical channel\n", getName());
129 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
133 reader->closeLogicalChannel();
136 mIsLogicalChannelOpen =
false;
137 mLogger->trace(
"Logical channel closed\n");
141LocalReaderAdapter::computeSelectApplicationP2(
142 const FileOccurrence fileOccurrence,
143 const FileControlInformation fileControlInformation)
147 switch (fileOccurrence) {
148 case FileOccurrence::FIRST:
151 case FileOccurrence::LAST:
154 case FileOccurrence::NEXT:
157 case FileOccurrence::PREVIOUS:
161 std::stringstream ss;
162 ss << fileOccurrence;
163 throw IllegalStateException(
"Unexpected value: " + ss.str());
166 switch (fileControlInformation) {
167 case FileControlInformation::FCI:
170 case FileControlInformation::FCP:
173 case FileControlInformation::FMD:
176 case FileControlInformation::NO_RESPONSE:
180 std::stringstream ss;
181 ss << fileControlInformation;
182 throw IllegalStateException(
"Unexpected value: " + ss.str());
188std::shared_ptr<ApduResponseAdapter>
189LocalReaderAdapter::processExplicitAidSelection(
190 std::shared_ptr<InternalIsoCardSelector> cardSelector)
192 const std::vector<uint8_t>& aid = cardSelector->getAid();
195 "Reader [%] selects application with AID [%]\n",
197 HexUtil::toHex(aid));
206 std::vector<uint8_t> selectApplicationCommand(6 + aid.size());
207 selectApplicationCommand[0] = 0x00;
208 selectApplicationCommand[1] = 0xA4;
209 selectApplicationCommand[2] = 0x04;
214 selectApplicationCommand[3] = computeSelectApplicationP2(
215 cardSelector->getFileOccurrence(),
216 cardSelector->getFileControlInformation());
217 selectApplicationCommand[4] =
static_cast<uint8_t
>(aid.size());
219 aid, 0, selectApplicationCommand, 5,
static_cast<int>(aid.size()));
220 selectApplicationCommand[5 + aid.size()] = 0x00;
222 auto apduRequest = std::shared_ptr<ApduRequest>(
223 new ApduRequest(selectApplicationCommand));
224 apduRequest->setInfo(
"Internal Select Application");
226 return processApduRequest(apduRequest);
229std::shared_ptr<ApduResponseAdapter>
230LocalReaderAdapter::selectByAid(
231 std::shared_ptr<InternalIsoCardSelector> cardSelector)
233 std::shared_ptr<ApduResponseAdapter> fciResponse =
nullptr;
239 Assert::getInstance().isInRange(
240 cardSelector->getAid().size(), 0, 16,
"aid");
243 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
245 const std::vector<uint8_t>& aid = cardSelector->getAid();
246 const uint8_t p2 = computeSelectApplicationP2(
247 cardSelector->getFileOccurrence(),
248 cardSelector->getFileControlInformation());
249 const std::vector<uint8_t> selectionDataBytes
250 = reader->openChannelForAid(aid, p2);
251 fciResponse = std::make_shared<ApduResponseAdapter>(selectionDataBytes);
253 fciResponse = processExplicitAidSelection(cardSelector);
260LocalReaderAdapter::checkPowerOnData(
261 const std::string& powerOnData,
262 std::shared_ptr<InternalCardSelector> cardSelector)
264 const std::string& powerOnDataRegex = cardSelector->getPowerOnDataRegex();
267 if (powerOnData !=
"" && powerOnDataRegex !=
""
268 && !std::regex_match(powerOnData, std::regex(powerOnDataRegex))) {
270 "Power-on data didn't match (powerOnData: %, powerOnDataRegex: %\n",
282std::shared_ptr<LocalReaderAdapter::SelectionStatus>
283LocalReaderAdapter::processSelection(
284 std::shared_ptr<CardSelectorBase> cardSelector,
285 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest)
289 std::string powerOnData =
"";
290 std::shared_ptr<ApduResponseAdapter> fciResponse =
nullptr;
291 bool hasMatched =
true;
293 auto internalSelector
294 = std::dynamic_pointer_cast<InternalCardSelector>(cardSelector);
295 if (!internalSelector) {
296 throw RuntimeException(
297 "cardSelector is not of type InternalCardSelector.");
300 const std::string& logicalProtocolName
301 = internalSelector->getLogicalProtocolName();
302 if (logicalProtocolName !=
"" && mUseDefaultProtocol) {
303 throw IllegalStateException(
304 "Protocol " + logicalProtocolName
305 +
" not associated to a reader protocol.");
309 if (logicalProtocolName ==
""
310 || logicalProtocolName == mCurrentLogicalProtocolName) {
316 powerOnData = mReaderSpi->getPowerOnData();
317 if (checkPowerOnData(powerOnData, internalSelector)) {
320 const auto internalIsoCardSelector
321 = std::dynamic_pointer_cast<InternalIsoCardSelector>(
323 if (internalIsoCardSelector
324 && internalIsoCardSelector->getAid().size() != 0) {
325 fciResponse = selectByAid(internalIsoCardSelector);
326 const std::vector<int>& statusWords
327 = cardSelectionRequest
328 ->getSuccessfulSelectionStatusWords();
329 hasMatched = std::find(
332 fciResponse->getStatusWord())
333 != statusWords.end();
335 fciResponse =
nullptr;
340 fciResponse =
nullptr;
345 fciResponse =
nullptr;
349 return std::make_shared<SelectionStatus>(
350 powerOnData, fciResponse, hasMatched);
352 }
catch (
const ReaderIOException& e) {
353 throw ReaderBrokenCommunicationException(
354 std::make_shared<CardResponseAdapter>(
355 std::vector<std::shared_ptr<ApduResponseApi>>({}),
false),
358 std::make_shared<ReaderIOException>(e));
360 }
catch (
const CardIOException& e) {
361 throw CardBrokenCommunicationException(
362 std::make_shared<CardResponseAdapter>(
363 std::vector<std::shared_ptr<ApduResponseApi>>({}),
false),
366 std::make_shared<CardIOException>(e));
370std::shared_ptr<CardSelectionResponseApi>
371LocalReaderAdapter::processCardSelectionRequest(
372 std::shared_ptr<CardSelectorBase> cardSelector,
373 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest,
374 const ChannelControl channelControl)
376 mIsLogicalChannelOpen =
false;
378 std::shared_ptr<SelectionStatus> selectionStatus(
379 processSelection(cardSelector, cardSelectionRequest));
380 if (!selectionStatus->mHasMatched) {
383 return std::make_shared<CardSelectionResponseAdapter>(
384 selectionStatus->mPowerOnData,
385 selectionStatus->mSelectApplicationResponse,
387 std::make_shared<CardResponseAdapter>(
388 std::vector<std::shared_ptr<ApduResponseApi>>({}),
false));
391 mIsLogicalChannelOpen =
true;
393 std::shared_ptr<CardResponseAdapter> cardResponse =
nullptr;
395 if (cardSelectionRequest->getCardRequest() !=
nullptr) {
397 = std::dynamic_pointer_cast<CardResponseAdapter>(processCardRequest(
398 cardSelectionRequest->getCardRequest(), channelControl));
400 cardResponse =
nullptr;
403 return std::make_shared<CardSelectionResponseAdapter>(
404 selectionStatus->mPowerOnData,
405 selectionStatus->mSelectApplicationResponse,
410std::shared_ptr<ApduResponseAdapter>
411LocalReaderAdapter::processApduRequest(
412 const std::shared_ptr<ApduRequestSpi> apduRequest)
414 std::shared_ptr<ApduResponseAdapter> apduResponse =
nullptr;
416 uint64_t timeStamp = System::nanoTime();
417 uint64_t elapsed10ms = (timeStamp - mBefore) / 100000;
421 "Reader [%] --> apduRequest: %, elapsed % ms\n",
426 apduResponse = std::make_shared<ApduResponseAdapter>(
427 mReaderSpi->transmitApdu(apduRequest->getApdu()));
429 timeStamp = System::nanoTime();
430 elapsed10ms = (timeStamp - mBefore) / 100000;
434 "Reader [%] <-- apduResponse: %, elapsed % ms\n",
439 if (mIsAutomaticStatusCodeHandlingEnabled) {
440 if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6100) {
446 std::vector<std::vector<uint8_t>> dataChunks;
449 if (apduResponse->getDataOut().size() > 0) {
450 dataChunks.push_back(apduResponse->getDataOut());
457 while ((apduResponse->getStatusWord() & SW1_MASK) == SW_6100) {
459 const uint8_t le = apduResponse->getStatusWord() & SW2_MASK;
460 const std::vector<uint8_t> getResponseApdu
461 = {0x00, 0xC0, 0x00, 0x00, le};
463 uint64_t chainedTimeStamp = System::nanoTime();
464 uint64_t chainedElapsed10ms
465 = (chainedTimeStamp - mBefore) / 100000;
466 mBefore = chainedTimeStamp;
469 "Reader [%] --> GET RESPONSE (chained): %, elapsed % ms\n",
471 HexUtil::toHex(getResponseApdu),
472 chainedElapsed10ms / 10.0);
475 apduResponse = std::make_shared<ApduResponseAdapter>(
476 mReaderSpi->transmitApdu(getResponseApdu));
478 chainedTimeStamp = System::nanoTime();
479 chainedElapsed10ms = (chainedTimeStamp - mBefore) / 100000;
480 mBefore = chainedTimeStamp;
483 "Reader [%] <-- apduResponse (chained): %, elapsed % ms\n",
486 chainedElapsed10ms / 10.0);
489 if (apduResponse->getDataOut().size() > 0) {
490 dataChunks.push_back(apduResponse->getDataOut());
498 if (!dataChunks.empty()) {
499 size_t totalLength = 0;
500 for (
const auto& chunk : dataChunks) {
501 totalLength += chunk.size();
505 std::vector<uint8_t> completeApdu(totalLength + 2);
507 for (
const auto& chunk : dataChunks) {
511 completeApdu.begin() + offset);
512 offset += chunk.size();
516 completeApdu[totalLength] =
static_cast<uint8_t
>(
517 (apduResponse->getStatusWord() >> 8) & 0xFF);
518 completeApdu[totalLength + 1] =
static_cast<uint8_t
>(
519 apduResponse->getStatusWord() & 0xFF);
522 = std::make_shared<ApduResponseAdapter>(completeApdu);
525 }
else if (apduResponse->getDataOut().size() == 0) {
529 if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6C00) {
534 std::vector<std::uint8_t> apdu = apduRequest->getApdu();
535 apdu[apduRequest->getApdu().size() - 1]
536 = (apduResponse->getStatusWord() & SW2_MASK);
537 apduRequest->setApdu(apdu);
540 apduResponse = processApduRequest(apduRequest);
543 ApduUtil::isCase4(apduRequest->getApdu())
545 apduRequest->getSuccessfulStatusWords(),
546 apduResponse->getStatusWord())) {
553 = apduRequest->getApdu()[apduRequest->getApdu().size() - 1];
554 const std::vector<uint8_t> getResponseApdu
555 = {0x00, 0xC0, 0x00, 0x00, le};
558 auto adapter = std::shared_ptr<ApduRequest>(
559 new ApduRequest(getResponseApdu));
560 adapter->setInfo(
"Internal Get Response");
561 apduResponse = processApduRequest(adapter);
570LocalReaderAdapter::releaseChannel()
575 mReaderSpi->closePhysicalChannel();
576 }
catch (
const ReaderIOException& e) {
577 throw ReaderBrokenCommunicationException(
580 "Failed to release the physical channel",
581 std::make_shared<ReaderIOException>(e));
586LocalReaderAdapter::deactivateReaderProtocol(
const std::string& readerProtocol)
590 Assert::getInstance().notEmpty(readerProtocol,
"readerProtocol");
592 mProtocolAssociations.erase(readerProtocol);
594 const auto configurable
595 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
596 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
597 throw ReaderProtocolNotSupportedException(readerProtocol);
600 configurable->deactivateProtocol(readerProtocol);
604LocalReaderAdapter::activateReaderProtocol(
605 const std::string& readerProtocol,
const std::string& applicationProtocol)
609 Assert::getInstance()
610 .notEmpty(readerProtocol,
"readerProtocol")
611 .notEmpty(applicationProtocol,
"applicationProtocol");
613 const auto configurable
614 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
615 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
616 throw ReaderProtocolNotSupportedException(readerProtocol);
619 configurable->activateProtocol(readerProtocol);
621 mProtocolAssociations.insert({readerProtocol, applicationProtocol});
625LocalReaderAdapter::isCardPresent()
634 return mReaderSpi->checkCardPresence();
635 }
catch (
const ReaderIOException& e) {
636 throw ReaderCommunicationException(
637 "An exception occurred while checking the card presence", e);
642LocalReaderAdapter::isContactless()
644 return mReaderSpi->isContactless();
647std::shared_ptr<CardResponseApi>
648LocalReaderAdapter::processCardRequest(
649 const std::shared_ptr<CardRequestSpi> cardRequest,
650 const ChannelControl channelControl)
655 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses;
657 for (
const auto& apduRequest : cardRequest->getApduRequests()) {
659 const std::shared_ptr<ApduResponseAdapter> apduResponse(
660 processApduRequest(apduRequest));
661 apduResponses.push_back(apduResponse);
662 if (cardRequest->stopOnUnsuccessfulStatusWord()
663 && !Arrays::contains(
664 apduRequest->getSuccessfulStatusWords(),
665 apduResponse->getStatusWord())) {
666 if (channelControl == ChannelControl::CLOSE_AFTER) {
667 closeLogicalAndPhysicalChannelsSilently();
669 throw UnexpectedStatusWordException(
670 std::make_shared<CardResponseAdapter>(apduResponses,
false),
671 cardRequest->getApduRequests().size()
672 == apduResponses.size(),
673 "Unexpected status word");
676 }
catch (
const ReaderIOException& e) {
677 closeLogicalAndPhysicalChannelsSilently();
678 throw ReaderBrokenCommunicationException(
679 std::make_shared<CardResponseAdapter>(apduResponses,
false),
681 "Failed to communicate with reader. Unable to transmit card "
683 std::make_shared<ReaderIOException>(e));
685 }
catch (
const CardIOException& e) {
686 closeLogicalAndPhysicalChannelsSilently();
687 throw CardBrokenCommunicationException(
688 std::make_shared<CardResponseAdapter>(apduResponses,
false),
690 "Failed to communicate with card. Unable to transmit card "
692 std::make_shared<CardIOException>(e));
697 if (channelControl == ChannelControl::CLOSE_AFTER) {
701 return std::make_shared<CardResponseAdapter>(
702 apduResponses, mIsLogicalChannelOpen);
705std::vector<std::shared_ptr<CardSelectionResponseApi>>
706LocalReaderAdapter::processCardSelectionRequests(
707 const std::vector<std::shared_ptr<CardSelectorBase>>& cardSelectors,
708 const std::vector<std::shared_ptr<CardSelectionRequestSpi>>&
709 cardSelectionRequests,
710 const MultiSelectionProcessing multiSelectionProcessing,
711 const ChannelControl channelControl)
716 if (!mReaderSpi->isPhysicalChannelOpen()) {
718 mReaderSpi->openPhysicalChannel();
719 computeCurrentProtocol();
720 }
catch (
const ReaderIOException& e) {
721 throw ReaderBrokenCommunicationException(
724 "Failed to communicate with reader. Unable to open physical "
726 std::make_shared<ReaderIOException>(e));
727 }
catch (
const CardIOException& e) {
728 throw CardBrokenCommunicationException(
731 "Failed to communicate with card. Unable to open physical "
733 std::make_shared<CardIOException>(e));
737 std::vector<std::shared_ptr<CardSelectionResponseApi>>
738 cardSelectionResponses;
742 = std::make_pair(cardSelectors.begin(), cardSelectionRequests.begin());
743 p.second != cardSelectionRequests.end()
744 && p.first != cardSelectors.end();
745 ++p.first, ++p.second) {
747 const auto cardSelectionResponse
748 = processCardSelectionRequest(*p.first, *p.second, channelControl);
749 cardSelectionResponses.push_back(cardSelectionResponse);
751 if (multiSelectionProcessing == MultiSelectionProcessing::PROCESS_ALL) {
756 closeLogicalChannel();
758 if (mIsLogicalChannelOpen) {
766 if (channelControl == ChannelControl::CLOSE_AFTER) {
770 return cardSelectionResponses;
774LocalReaderAdapter::doUnregister()
777 mReaderSpi->closePhysicalChannel();
778 }
catch (
const Exception& e) {
780 "[reader=%] Failed to close physical channel [reason=%]\n",
786 mReaderSpi->onUnregister();
787 }
catch (
const Exception& e) {
789 "[reader=%] Failed to unregister reader extension [reason=%]\n",
794 AbstractReaderAdapter::doUnregister();
798LocalReaderAdapter::closeLogicalAndPhysicalChannelsSilently()
800 closeLogicalChannel();
803 mCurrentLogicalProtocolName =
"";
804 mUseDefaultProtocol =
false;
807 mReaderSpi->closePhysicalChannel();
808 }
catch (
const ReaderIOException& e) {
810 "[reader=%] Failed to close physical channel [reason=%]\n",
817LocalReaderAdapter::isLogicalChannelOpen()
const
819 return mIsLogicalChannelOpen;
822std::shared_ptr<ReaderSpi>
823LocalReaderAdapter::getReaderSpi()
const
832LocalReaderAdapter::SelectionStatus::SelectionStatus(
833 const std::string& powerOnData,
834 const std::shared_ptr<ApduResponseAdapter> selectApplicationResponse,
835 const bool hasMatched)
836: mPowerOnData(powerOnData)
837, mSelectApplicationResponse(selectApplicationResponse)
838, mHasMatched(hasMatched)
846const int LocalReaderAdapter::ApduRequest::DEFAULT_SUCCESSFUL_CODE = 0x9000;
848LocalReaderAdapter::ApduRequest::ApduRequest(
const std::vector<uint8_t>& apdu)
850, mSuccessfulStatusWords({DEFAULT_SUCCESSFUL_CODE})
854LocalReaderAdapter::ApduRequest&
855LocalReaderAdapter::ApduRequest::setInfo(
const std::string& info)
862const std::vector<uint8_t>&
863LocalReaderAdapter::ApduRequest::getApdu()
const
869LocalReaderAdapter::ApduRequest::setApdu(
const std::vector<std::uint8_t>& apdu)
874const std::vector<int>&
875LocalReaderAdapter::ApduRequest::getSuccessfulStatusWords()
const
877 return mSuccessfulStatusWords;
881LocalReaderAdapter::ApduRequest::getInfo()
const
887operator<<(std::ostream& os, LocalReaderAdapter::ApduRequest& ar)
889 os <<
"APDU_REQUEST: {"
890 <<
"APDU: " << ar.getApdu() <<
", "
891 <<
"SUCCESSFUL_STATUS_WORDS: " << ar.getSuccessfulStatusWords() <<
", "
892 <<
"INFO: " << ar.getInfo() <<
"}";
899 std::ostream& os,
const std::shared_ptr<LocalReaderAdapter::ApduRequest> ar)
902 os <<
"APDU_REQUEST: null";
913 const std::vector<std::shared_ptr<LocalReaderAdapter::ApduRequest>>& ars)
915 os <<
"APDU_REQUESTS: {";
917 for (
auto it = ars.begin(); it != ars.end(); it++) {
918 if (it != ars.begin()) {
std::ostream & operator<<(std::ostream &os, const std::vector< std::shared_ptr< LocalReaderAdapter::ApduRequest > > &ars)