Keyple Service C++ Library - 3.4.1
Component of the Keyple C++ middleware
LocalReaderAdapter.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/core/service/LocalReaderAdapter.hpp"
15
16#include <memory>
17#include <regex>
18#include <sstream>
19#include <string>
20#include <utility>
21#include <vector>
22
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"
43
44namespace keyple {
45namespace core {
46namespace service {
47
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;
65
66/* LOCAL READER ADAPTER
67 * ------------------------------------------------------------------------- */
68
69const int LocalReaderAdapter::SW_6100 = 0x6100;
70
71const int LocalReaderAdapter::SW_6C00 = 0x6C00;
72
73const int LocalReaderAdapter::SW1_MASK = 0xFF00;
74const int LocalReaderAdapter::SW2_MASK = 0x00FF;
75
76LocalReaderAdapter::LocalReaderAdapter(
77 std::shared_ptr<ReaderSpi> readerSpi, const std::string& pluginName)
78: AbstractReaderAdapter(
79 readerSpi->getName(),
80 std::dynamic_pointer_cast<KeypleReaderExtension>(readerSpi),
81 pluginName)
82, mReaderSpi(readerSpi)
83, mBefore(0)
84, mIsLogicalChannelOpen(false)
85, mUseDefaultProtocol(false)
86, mCurrentLogicalProtocolName("")
87, mCurrentPhysicalProtocolName("")
88, mProtocolAssociations({})
89, mIsAutomaticStatusCodeHandlingEnabled(
90 SmartCardServiceProvider::getService()
91 ->isAutomaticStatusCodeHandlingEnabled())
92{
93}
94
95void
96LocalReaderAdapter::computeCurrentProtocol()
97{
98 mCurrentLogicalProtocolName = "";
99 mCurrentPhysicalProtocolName = "";
100
101 if (mProtocolAssociations.empty()) {
102 mUseDefaultProtocol = true;
103 } else {
104 mUseDefaultProtocol = false;
105
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;
112 }
113 }
114 }
115}
116
117const std::string&
118LocalReaderAdapter::getCurrentPhysicalProtocolName() const
119{
120 return mCurrentPhysicalProtocolName;
121}
122
123void
124LocalReaderAdapter::closeLogicalChannel()
125{
126 mLogger->trace("Reader [%] closes logical channel\n", getName());
127
128 auto reader
129 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
130 if (reader) {
131 /* AutonomousSelectionReader have an explicit method for closing
132 * channels */
133 reader->closeLogicalChannel();
134 }
135
136 mIsLogicalChannelOpen = false;
137 mLogger->trace("Logical channel closed\n");
138}
139
140uint8_t
141LocalReaderAdapter::computeSelectApplicationP2(
142 const FileOccurrence fileOccurrence,
143 const FileControlInformation fileControlInformation)
144{
145 uint8_t p2;
146
147 switch (fileOccurrence) {
148 case FileOccurrence::FIRST:
149 p2 = 0x00;
150 break;
151 case FileOccurrence::LAST:
152 p2 = 0x01;
153 break;
154 case FileOccurrence::NEXT:
155 p2 = 0x02;
156 break;
157 case FileOccurrence::PREVIOUS:
158 p2 = 0x03;
159 break;
160 default:
161 std::stringstream ss;
162 ss << fileOccurrence;
163 throw IllegalStateException("Unexpected value: " + ss.str());
164 }
165
166 switch (fileControlInformation) {
167 case FileControlInformation::FCI:
168 p2 |= 0x00;
169 break;
170 case FileControlInformation::FCP:
171 p2 |= 0x04;
172 break;
173 case FileControlInformation::FMD:
174 p2 |= 0x08;
175 break;
176 case FileControlInformation::NO_RESPONSE:
177 p2 |= 0x0C;
178 break;
179 default:
180 std::stringstream ss;
181 ss << fileControlInformation;
182 throw IllegalStateException("Unexpected value: " + ss.str());
183 }
184
185 return p2;
186}
187
188std::shared_ptr<ApduResponseAdapter>
189LocalReaderAdapter::processExplicitAidSelection(
190 std::shared_ptr<InternalIsoCardSelector> cardSelector)
191{
192 const std::vector<uint8_t>& aid = cardSelector->getAid();
193
194 mLogger->debug(
195 "Reader [%] selects application with AID [%]\n",
196 getName(),
197 HexUtil::toHex(aid));
198
199 /*
200 * Build a get response command the actual length expected by the card in
201 * the get response command is handled in transmitApdu
202 *
203 * RL-SEL-CLA.1
204 * RL-SEL-P2LC.1
205 */
206 std::vector<uint8_t> selectApplicationCommand(6 + aid.size());
207 selectApplicationCommand[0] = 0x00; /* CLA */
208 selectApplicationCommand[1] = 0xA4; /* INS */
209 selectApplicationCommand[2] = 0x04; /* P1: select by name */
210 /*
211 * P2: b0,b1 define the File occurrence, b2,b3 define the File control
212 * information we use the bitmask defined in the respective enums
213 */
214 selectApplicationCommand[3] = computeSelectApplicationP2(
215 cardSelector->getFileOccurrence(),
216 cardSelector->getFileControlInformation());
217 selectApplicationCommand[4] = static_cast<uint8_t>(aid.size()); /* Lc */
218 System::arraycopy(
219 aid, 0, selectApplicationCommand, 5, static_cast<int>(aid.size()));
220 selectApplicationCommand[5 + aid.size()] = 0x00; /* Le */
221
222 auto apduRequest = std::shared_ptr<ApduRequest>(
223 new ApduRequest(selectApplicationCommand));
224 apduRequest->setInfo("Internal Select Application");
225
226 return processApduRequest(apduRequest);
227}
228
229std::shared_ptr<ApduResponseAdapter>
230LocalReaderAdapter::selectByAid(
231 std::shared_ptr<InternalIsoCardSelector> cardSelector)
232{
233 std::shared_ptr<ApduResponseAdapter> fciResponse = nullptr;
234
235 /*
236 * RL-SEL-P2LC.1
237 * RL-SEL-DFNAME.1
238 */
239 Assert::getInstance().isInRange(
240 cardSelector->getAid().size(), 0, 16, "aid");
241
242 auto reader
243 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
244 if (reader) {
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);
252 } else {
253 fciResponse = processExplicitAidSelection(cardSelector);
254 }
255
256 return fciResponse;
257}
258
259bool
260LocalReaderAdapter::checkPowerOnData(
261 const std::string& powerOnData,
262 std::shared_ptr<InternalCardSelector> cardSelector)
263{
264 const std::string& powerOnDataRegex = cardSelector->getPowerOnDataRegex();
265
266 /* Check the power-on data */
267 if (powerOnData != "" && powerOnDataRegex != ""
268 && !std::regex_match(powerOnData, std::regex(powerOnDataRegex))) {
269 mLogger->trace(
270 "Power-on data didn't match (powerOnData: %, powerOnDataRegex: %\n",
271 getName(),
272 powerOnDataRegex);
273
274 /* The power-on data have been rejected */
275 return false;
276 } else {
277 /* The power-on data have been accepted */
278 return true;
279 }
280}
281
282std::shared_ptr<LocalReaderAdapter::SelectionStatus>
283LocalReaderAdapter::processSelection(
284 std::shared_ptr<CardSelectorBase> cardSelector,
285 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest)
286{
287 try {
288 /* RL-CLA-CHAAUTO.1 */
289 std::string powerOnData = "";
290 std::shared_ptr<ApduResponseAdapter> fciResponse = nullptr;
291 bool hasMatched = true;
292
293 auto internalSelector
294 = std::dynamic_pointer_cast<InternalCardSelector>(cardSelector);
295 if (!internalSelector) {
296 throw RuntimeException(
297 "cardSelector is not of type InternalCardSelector.");
298 }
299
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.");
306 }
307
308 /* Check protocol if enabled */
309 if (logicalProtocolName == ""
310 || logicalProtocolName == mCurrentLogicalProtocolName) {
311 /*
312 * Protocol check succeeded, check power-on data if enabled
313 * RL-ATR-FILTER
314 * RL-SEL-USAGE.1
315 */
316 powerOnData = mReaderSpi->getPowerOnData();
317 if (checkPowerOnData(powerOnData, internalSelector)) {
318 /* No power-on data filter or power-on data check succeeded,
319 * select by AID if enabled */
320 const auto internalIsoCardSelector
321 = std::dynamic_pointer_cast<InternalIsoCardSelector>(
322 cardSelector);
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(
330 statusWords.begin(),
331 statusWords.end(),
332 fciResponse->getStatusWord())
333 != statusWords.end();
334 } else {
335 fciResponse = nullptr;
336 }
337 } else {
338 /* Check failed */
339 hasMatched = false;
340 fciResponse = nullptr;
341 }
342 } else {
343 /* Protocol failed */
344 powerOnData = "";
345 fciResponse = nullptr;
346 hasMatched = false;
347 }
348
349 return std::make_shared<SelectionStatus>(
350 powerOnData, fciResponse, hasMatched);
351
352 } catch (const ReaderIOException& e) {
353 throw ReaderBrokenCommunicationException(
354 std::make_shared<CardResponseAdapter>(
355 std::vector<std::shared_ptr<ApduResponseApi>>({}), false),
356 false,
357 e.getMessage(),
358 std::make_shared<ReaderIOException>(e));
359
360 } catch (const CardIOException& e) {
361 throw CardBrokenCommunicationException(
362 std::make_shared<CardResponseAdapter>(
363 std::vector<std::shared_ptr<ApduResponseApi>>({}), false),
364 false,
365 e.getMessage(),
366 std::make_shared<CardIOException>(e));
367 }
368}
369
370std::shared_ptr<CardSelectionResponseApi>
371LocalReaderAdapter::processCardSelectionRequest(
372 std::shared_ptr<CardSelectorBase> cardSelector,
373 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest,
374 const ChannelControl channelControl)
375{
376 mIsLogicalChannelOpen = false;
377
378 std::shared_ptr<SelectionStatus> selectionStatus(
379 processSelection(cardSelector, cardSelectionRequest));
380 if (!selectionStatus->mHasMatched) {
381 /* The selection failed, return an empty response having the selection
382 * status */
383 return std::make_shared<CardSelectionResponseAdapter>(
384 selectionStatus->mPowerOnData,
385 selectionStatus->mSelectApplicationResponse,
386 false,
387 std::make_shared<CardResponseAdapter>(
388 std::vector<std::shared_ptr<ApduResponseApi>>({}), false));
389 }
390
391 mIsLogicalChannelOpen = true;
392
393 std::shared_ptr<CardResponseAdapter> cardResponse = nullptr;
394
395 if (cardSelectionRequest->getCardRequest() != nullptr) {
396 cardResponse
397 = std::dynamic_pointer_cast<CardResponseAdapter>(processCardRequest(
398 cardSelectionRequest->getCardRequest(), channelControl));
399 } else {
400 cardResponse = nullptr;
401 }
402
403 return std::make_shared<CardSelectionResponseAdapter>(
404 selectionStatus->mPowerOnData,
405 selectionStatus->mSelectApplicationResponse,
406 true,
407 cardResponse);
408}
409
410std::shared_ptr<ApduResponseAdapter>
411LocalReaderAdapter::processApduRequest(
412 const std::shared_ptr<ApduRequestSpi> apduRequest)
413{
414 std::shared_ptr<ApduResponseAdapter> apduResponse = nullptr;
415
416 uint64_t timeStamp = System::nanoTime();
417 uint64_t elapsed10ms = (timeStamp - mBefore) / 100000;
418 mBefore = timeStamp;
419
420 mLogger->debug(
421 "Reader [%] --> apduRequest: %, elapsed % ms\n",
422 getName(),
423 apduRequest,
424 elapsed10ms / 10.0);
425
426 apduResponse = std::make_shared<ApduResponseAdapter>(
427 mReaderSpi->transmitApdu(apduRequest->getApdu()));
428
429 timeStamp = System::nanoTime();
430 elapsed10ms = (timeStamp - mBefore) / 100000;
431 mBefore = timeStamp;
432
433 mLogger->debug(
434 "Reader [%] <-- apduResponse: %, elapsed % ms\n",
435 getName(),
436 apduResponse,
437 elapsed10ms / 10.0);
438
439 if (mIsAutomaticStatusCodeHandlingEnabled) {
440 if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6100) {
441 /*
442 * RL-SW-61XX.1
443 * Handle chained responses by accumulating data from multiple
444 * GET RESPONSE commands
445 */
446 std::vector<std::vector<uint8_t>> dataChunks;
447
448 /* Add initial data if present */
449 if (apduResponse->getDataOut().size() > 0) {
450 dataChunks.push_back(apduResponse->getDataOut());
451 }
452
453 /*
454 * Keep sending GET RESPONSE until we get a status word other
455 * than 61XX
456 */
457 while ((apduResponse->getStatusWord() & SW1_MASK) == SW_6100) {
458 /* Build a GetResponse APDU command with the length from SW2 */
459 const uint8_t le = apduResponse->getStatusWord() & SW2_MASK;
460 const std::vector<uint8_t> getResponseApdu
461 = {0x00, 0xC0, 0x00, 0x00, le};
462
463 uint64_t chainedTimeStamp = System::nanoTime();
464 uint64_t chainedElapsed10ms
465 = (chainedTimeStamp - mBefore) / 100000;
466 mBefore = chainedTimeStamp;
467
468 mLogger->debug(
469 "Reader [%] --> GET RESPONSE (chained): %, elapsed % ms\n",
470 getName(),
471 HexUtil::toHex(getResponseApdu),
472 chainedElapsed10ms / 10.0);
473
474 /* Execute APDU directly to avoid recursive status handling */
475 apduResponse = std::make_shared<ApduResponseAdapter>(
476 mReaderSpi->transmitApdu(getResponseApdu));
477
478 chainedTimeStamp = System::nanoTime();
479 chainedElapsed10ms = (chainedTimeStamp - mBefore) / 100000;
480 mBefore = chainedTimeStamp;
481
482 mLogger->debug(
483 "Reader [%] <-- apduResponse (chained): %, elapsed % ms\n",
484 getName(),
485 apduResponse,
486 chainedElapsed10ms / 10.0);
487
488 /* Add data from this response */
489 if (apduResponse->getDataOut().size() > 0) {
490 dataChunks.push_back(apduResponse->getDataOut());
491 }
492 }
493
494 /*
495 * Merge all data chunks into a single response with the final
496 * status word
497 */
498 if (!dataChunks.empty()) {
499 size_t totalLength = 0;
500 for (const auto& chunk : dataChunks) {
501 totalLength += chunk.size();
502 }
503
504 /* +2 for status word */
505 std::vector<uint8_t> completeApdu(totalLength + 2);
506 size_t offset = 0;
507 for (const auto& chunk : dataChunks) {
508 std::copy(
509 chunk.begin(),
510 chunk.end(),
511 completeApdu.begin() + offset);
512 offset += chunk.size();
513 }
514
515 /* Append final status word */
516 completeApdu[totalLength] = static_cast<uint8_t>(
517 (apduResponse->getStatusWord() >> 8) & 0xFF);
518 completeApdu[totalLength + 1] = static_cast<uint8_t>(
519 apduResponse->getStatusWord() & 0xFF);
520
521 apduResponse
522 = std::make_shared<ApduResponseAdapter>(completeApdu);
523 }
524
525 } else if (apduResponse->getDataOut().size() == 0) {
526 /* Handle 6CXX and Case4 only when there's no data in the response
527 */
528
529 if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6C00) {
530 /*
531 * RL-SW-6CXX.1
532 * Update the last command with the provided "le"
533 */
534 std::vector<std::uint8_t> apdu = apduRequest->getApdu();
535 apdu[apduRequest->getApdu().size() - 1]
536 = (apduResponse->getStatusWord() & SW2_MASK);
537 apduRequest->setApdu(apdu);
538
539 /* Replay the last command APDU */
540 apduResponse = processApduRequest(apduRequest);
541
542 } else if (
543 ApduUtil::isCase4(apduRequest->getApdu())
544 && Arrays::contains(
545 apduRequest->getSuccessfulStatusWords(),
546 apduResponse->getStatusWord())) {
547 /*
548 * RL-SW-ANALYSIS.1
549 * RL-SW-CASE4.1 (SW=6200 not taken into account here)
550 * Build a GetResponse APDU command with the original "le"
551 */
552 const uint8_t le
553 = apduRequest->getApdu()[apduRequest->getApdu().size() - 1];
554 const std::vector<uint8_t> getResponseApdu
555 = {0x00, 0xC0, 0x00, 0x00, le};
556
557 /* Execute GetResponse APDU */
558 auto adapter = std::shared_ptr<ApduRequest>(
559 new ApduRequest(getResponseApdu));
560 adapter->setInfo("Internal Get Response");
561 apduResponse = processApduRequest(adapter);
562 }
563 }
564 }
565
566 return apduResponse;
567}
568
569void
570LocalReaderAdapter::releaseChannel()
571{
572 checkStatus();
573
574 try {
575 mReaderSpi->closePhysicalChannel();
576 } catch (const ReaderIOException& e) {
577 throw ReaderBrokenCommunicationException(
578 nullptr,
579 false,
580 "Failed to release the physical channel",
581 std::make_shared<ReaderIOException>(e));
582 }
583}
584
585void
586LocalReaderAdapter::deactivateReaderProtocol(const std::string& readerProtocol)
587{
588 /* RL-CL-PROTOCOL.1 */
589 checkStatus();
590 Assert::getInstance().notEmpty(readerProtocol, "readerProtocol");
591
592 mProtocolAssociations.erase(readerProtocol);
593
594 const auto configurable
595 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
596 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
597 throw ReaderProtocolNotSupportedException(readerProtocol);
598 }
599
600 configurable->deactivateProtocol(readerProtocol);
601}
602
603void
604LocalReaderAdapter::activateReaderProtocol(
605 const std::string& readerProtocol, const std::string& applicationProtocol)
606{
607 /* RL-CL-PROTOCOL.1 */
608 checkStatus();
609 Assert::getInstance()
610 .notEmpty(readerProtocol, "readerProtocol")
611 .notEmpty(applicationProtocol, "applicationProtocol");
612
613 const auto configurable
614 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
615 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
616 throw ReaderProtocolNotSupportedException(readerProtocol);
617 }
618
619 configurable->activateProtocol(readerProtocol);
620
621 mProtocolAssociations.insert({readerProtocol, applicationProtocol});
622}
623
624bool
625LocalReaderAdapter::isCardPresent()
626{
627 /*
628 * RL-DET-PCRQ.1
629 * RL-DET-PCAPDU.1
630 */
631 checkStatus();
632
633 try {
634 return mReaderSpi->checkCardPresence();
635 } catch (const ReaderIOException& e) {
636 throw ReaderCommunicationException(
637 "An exception occurred while checking the card presence", e);
638 }
639}
640
641bool
642LocalReaderAdapter::isContactless()
643{
644 return mReaderSpi->isContactless();
645}
646
647std::shared_ptr<CardResponseApi>
648LocalReaderAdapter::processCardRequest(
649 const std::shared_ptr<CardRequestSpi> cardRequest,
650 const ChannelControl channelControl)
651{
652 checkStatus();
653
654 /* Proceeds with the APDU requests present in the CardRequest */
655 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses;
656
657 for (const auto& apduRequest : cardRequest->getApduRequests()) {
658 try {
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();
668 }
669 throw UnexpectedStatusWordException(
670 std::make_shared<CardResponseAdapter>(apduResponses, false),
671 cardRequest->getApduRequests().size()
672 == apduResponses.size(),
673 "Unexpected status word");
674 }
675
676 } catch (const ReaderIOException& e) {
677 closeLogicalAndPhysicalChannelsSilently();
678 throw ReaderBrokenCommunicationException(
679 std::make_shared<CardResponseAdapter>(apduResponses, false),
680 false,
681 "Failed to communicate with reader. Unable to transmit card "
682 "request",
683 std::make_shared<ReaderIOException>(e));
684
685 } catch (const CardIOException& e) {
686 closeLogicalAndPhysicalChannelsSilently();
687 throw CardBrokenCommunicationException(
688 std::make_shared<CardResponseAdapter>(apduResponses, false),
689 false,
690 "Failed to communicate with card. Unable to transmit card "
691 "request",
692 std::make_shared<CardIOException>(e));
693 }
694 }
695
696 /* Close the channel if requested */
697 if (channelControl == ChannelControl::CLOSE_AFTER) {
698 releaseChannel();
699 }
700
701 return std::make_shared<CardResponseAdapter>(
702 apduResponses, mIsLogicalChannelOpen);
703}
704
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)
712{
713 checkStatus();
714
715 /* Open the physical channel, determine the current protocol */
716 if (!mReaderSpi->isPhysicalChannelOpen()) {
717 try {
718 mReaderSpi->openPhysicalChannel();
719 computeCurrentProtocol();
720 } catch (const ReaderIOException& e) {
721 throw ReaderBrokenCommunicationException(
722 nullptr,
723 false,
724 "Failed to communicate with reader. Unable to open physical "
725 "channel",
726 std::make_shared<ReaderIOException>(e));
727 } catch (const CardIOException& e) {
728 throw CardBrokenCommunicationException(
729 nullptr,
730 false,
731 "Failed to communicate with card. Unable to open physical "
732 "channel",
733 std::make_shared<CardIOException>(e));
734 }
735 }
736
737 std::vector<std::shared_ptr<CardSelectionResponseApi>>
738 cardSelectionResponses;
739
740 /* Loop over all CardRequest provided in the list */
741 for (auto p
742 = std::make_pair(cardSelectors.begin(), cardSelectionRequests.begin());
743 p.second != cardSelectionRequests.end()
744 && p.first != cardSelectors.end();
745 ++p.first, ++p.second) {
746 /* Process the CardRequest and append the CardResponse list */
747 const auto cardSelectionResponse
748 = processCardSelectionRequest(*p.first, *p.second, channelControl);
749 cardSelectionResponses.push_back(cardSelectionResponse);
750
751 if (multiSelectionProcessing == MultiSelectionProcessing::PROCESS_ALL) {
752 /*
753 * Multi CardRequest case: just close the logical channel and go on
754 * with the next selection.
755 */
756 closeLogicalChannel();
757 } else {
758 if (mIsLogicalChannelOpen) {
759 /* The logical channel being open, we stop here */
760 break; /* Exit for loop */
761 }
762 }
763 }
764
765 /* Close the channel if requested */
766 if (channelControl == ChannelControl::CLOSE_AFTER) {
767 releaseChannel();
768 }
769
770 return cardSelectionResponses;
771}
772
773void
774LocalReaderAdapter::doUnregister()
775{
776 try {
777 mReaderSpi->closePhysicalChannel();
778 } catch (const Exception& e) {
779 mLogger->warn(
780 "[reader=%] Failed to close physical channel [reason=%]\n",
781 getName(),
782 e.getMessage());
783 }
784
785 try {
786 mReaderSpi->onUnregister();
787 } catch (const Exception& e) {
788 mLogger->warn(
789 "[reader=%] Failed to unregister reader extension [reason=%]\n",
790 getName(),
791 e.getMessage());
792 }
793
794 AbstractReaderAdapter::doUnregister();
795}
796
797void
798LocalReaderAdapter::closeLogicalAndPhysicalChannelsSilently()
799{
800 closeLogicalChannel();
801
802 /* Closes the physical channel and resets the current protocol info */
803 mCurrentLogicalProtocolName = "";
804 mUseDefaultProtocol = false;
805
806 try {
807 mReaderSpi->closePhysicalChannel();
808 } catch (const ReaderIOException& e) {
809 mLogger->error(
810 "[reader=%] Failed to close physical channel [reason=%]\n",
811 getName(),
812 e.getMessage());
813 }
814}
815
816bool
817LocalReaderAdapter::isLogicalChannelOpen() const
818{
819 return mIsLogicalChannelOpen;
820}
821
822std::shared_ptr<ReaderSpi>
823LocalReaderAdapter::getReaderSpi() const
824{
825 return mReaderSpi;
826}
827
828/* SELECTION STATUS
829 * -----------------------------------------------------------------------------
830 */
831
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)
839{
840}
841
842/* APDU REQUEST
843 * ---------------------------------------------------------------------------------
844 */
845
846const int LocalReaderAdapter::ApduRequest::DEFAULT_SUCCESSFUL_CODE = 0x9000;
847
848LocalReaderAdapter::ApduRequest::ApduRequest(const std::vector<uint8_t>& apdu)
849: mApdu(apdu)
850, mSuccessfulStatusWords({DEFAULT_SUCCESSFUL_CODE})
851{
852}
853
854LocalReaderAdapter::ApduRequest&
855LocalReaderAdapter::ApduRequest::setInfo(const std::string& info)
856{
857 mInfo = info;
858
859 return *this;
860}
861
862const std::vector<uint8_t>&
863LocalReaderAdapter::ApduRequest::getApdu() const
864{
865 return mApdu;
866}
867
868void
869LocalReaderAdapter::ApduRequest::setApdu(const std::vector<std::uint8_t>& apdu)
870{
871 mApdu = apdu;
872}
873
874const std::vector<int>&
875LocalReaderAdapter::ApduRequest::getSuccessfulStatusWords() const
876{
877 return mSuccessfulStatusWords;
878}
879
880const std::string&
881LocalReaderAdapter::ApduRequest::getInfo() const
882{
883 return mInfo;
884}
885
886std::ostream&
887operator<<(std::ostream& os, LocalReaderAdapter::ApduRequest& ar)
888{
889 os << "APDU_REQUEST: {"
890 << "APDU: " << ar.getApdu() << ", "
891 << "SUCCESSFUL_STATUS_WORDS: " << ar.getSuccessfulStatusWords() << ", "
892 << "INFO: " << ar.getInfo() << "}";
893
894 return os;
895}
896
897std::ostream&
899 std::ostream& os, const std::shared_ptr<LocalReaderAdapter::ApduRequest> ar)
900{
901 if (ar == nullptr) {
902 os << "APDU_REQUEST: null";
903 } else {
904 os << *ar;
905 }
906
907 return os;
908}
909
910std::ostream&
912 std::ostream& os,
913 const std::vector<std::shared_ptr<LocalReaderAdapter::ApduRequest>>& ars)
914{
915 os << "APDU_REQUESTS: {";
916
917 for (auto it = ars.begin(); it != ars.end(); it++) {
918 if (it != ars.begin()) {
919 os << ", ";
920 }
921 os << *it;
922 }
923
924 os << "}";
925
926 return os;
927}
928
929} /* namespace service */
930} /* namespace core */
931} /* namespace keyple */
std::ostream & operator<<(std::ostream &os, const std::vector< std::shared_ptr< LocalReaderAdapter::ApduRequest > > &ars)