Keyple Card Calypso C++ Library 2.1.0
Reference Terminal Reader API for C++
CardTransactionManagerAdapter.cpp
Go to the documentation of this file.
1/**************************************************************************************************
2 * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional information regarding *
5 * copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the terms of the Eclipse *
8 * Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 *
9 * *
10 * SPDX-License-Identifier: EPL-2.0 *
11 **************************************************************************************************/
12
14
15#include <algorithm>
16#include <sstream>
17
18/* Calypsonet Terminal Calypso */
19#include "AtomicTransactionException.h"
20#include "CardAnomalyException.h"
21#include "CardCloseSecureSessionException.h"
22#include "CardIOException.h"
23#include "DesynchronizedExchangesException.h"
24#include "SamAnomalyException.h"
25#include "SamAnomalyException.h"
26#include "SamIOException.h"
27#include "SessionAuthenticationException.h"
28#include "SvAuthenticationException.h"
29#include "UnauthorizedKeyException.h"
30
31/* Calypsonet Terminal Card */
32#include "ApduResponseApi.h"
33#include "CardBrokenCommunicationException.h"
34#include "CardResponseApi.h"
35#include "ReaderBrokenCommunicationException.h"
36#include "UnexpectedStatusWordException.h"
37
38/* Keyple Card Calypso */
39#include "CalypsoCardConstant.h"
44#include "CardRequestAdapter.h"
47#include "CmdCardGetDataFci.h"
48#include "CmdCardGetDataFcp.h"
49#include "CmdCardInvalidate.h"
50#include "CmdCardReadRecords.h"
51#include "CmdCardRehabilitate.h"
52#include "CmdCardSelectFile.h"
53
54/* Keyple Core Util */
55#include "Arrays.h"
56#include "ByteArrayUtil.h"
57#include "IllegalStateException.h"
58#include "KeypleAssert.h"
59#include "KeypleStd.h"
60#include "MapUtils.h"
61#include "UnsupportedOperationException.h"
63
64namespace keyple {
65namespace card {
66namespace calypso {
67
68using namespace calypsonet::terminal::calypso::transaction;
69using namespace calypsonet::terminal::card;
70using namespace keyple::core::util;
71using namespace keyple::core::util::cpp;
72using namespace keyple::core::util::cpp::exception;
73
74/* CARD TRANSACTION MANAGER ADAPTER ------------------------------------------------------------- */
75
76const std::string CardTransactionManagerAdapter::CARD_READER_COMMUNICATION_ERROR =
77 "A communication error with the card reader occurred while ";
78const std::string CardTransactionManagerAdapter::CARD_COMMUNICATION_ERROR =
79 "A communication error with the card occurred while ";
80const std::string CardTransactionManagerAdapter::CARD_COMMAND_ERROR =
81 "A card command error occurred while ";
82const std::string CardTransactionManagerAdapter::SAM_READER_COMMUNICATION_ERROR =
83 "A communication error with the SAM reader occurred while ";
84const std::string CardTransactionManagerAdapter::SAM_COMMUNICATION_ERROR =
85 "A communication error with the SAM occurred while ";
86const std::string CardTransactionManagerAdapter::SAM_COMMAND_ERROR =
87 "A SAM command error occurred while ";
88const std::string CardTransactionManagerAdapter::PIN_NOT_AVAILABLE_ERROR =
89 "PIN is not available for this card.";
90const std::string CardTransactionManagerAdapter::GENERATING_OF_THE_PIN_CIPHERED_DATA_ERROR =
91 "generating of the PIN ciphered data.";
92const std::string CardTransactionManagerAdapter::GENERATING_OF_THE_KEY_CIPHERED_DATA_ERROR =
93 "generating of the key ciphered data.";
94const std::string CardTransactionManagerAdapter::TRANSMITTING_COMMANDS =
95 "transmitting commands.";
96const std::string CardTransactionManagerAdapter::CHECKING_THE_SV_OPERATION =
97 "checking the SV operation.";
98const std::string CardTransactionManagerAdapter::UNEXPECTED_EXCEPTION =
99 "An unexpected exception was raised.";
100const std::string CardTransactionManagerAdapter::RECORD_NUMBER = "recordNumber";
101
102const int CardTransactionManagerAdapter::SESSION_BUFFER_CMD_ADDITIONAL_COST = 6;
103const int CardTransactionManagerAdapter::APDU_HEADER_LENGTH = 5;
104
105const std::string CardTransactionManagerAdapter::OFFSET = "offset";
106
107const std::shared_ptr<ApduResponseApi> CardTransactionManagerAdapter::RESPONSE_OK =
108 std::make_shared<ApduResponseAdapter>(std::vector<uint8_t>({0x90, 0x00}));
109const std::shared_ptr<ApduResponseApi> CardTransactionManagerAdapter::RESPONSE_OK_POSTPONED =
110 std::make_shared<ApduResponseAdapter>(std::vector<uint8_t>({0x62, 0x00}));
111
113 const std::shared_ptr<CardReader> cardReader,
114 const std::shared_ptr<CalypsoCard> calypsoCard,
115 const std::shared_ptr<CardSecuritySetting> cardSecuritySetting)
116: mCardReader(std::dynamic_pointer_cast<ProxyReaderApi>(cardReader)),
117 mCardSecuritySettings(cardSecuritySetting),
118 mSamCommandProcessor(cardSecuritySetting ?
119 std::make_shared<SamCommandProcessor>(calypsoCard, cardSecuritySetting) :
120 nullptr),
121 mCalypsoCard(std::dynamic_pointer_cast<CalypsoCardAdapter>(calypsoCard)),
122 mSessionState(SessionState::SESSION_UNINITIALIZED),
123 mCurrentWriteAccessLevel(WriteAccessLevel::DEBIT), /* had to set a default value to please MSVC */
124 mModificationsCounter(mCalypsoCard->getModificationsCounter()),
125 mCardCommandManager(std::make_shared<CardCommandManager>()),
126 mSvAction(SvAction::DO), /* had to set a default value to please MSVC */
127 mChannelControl(ChannelControl::KEEP_OPEN) {}
128
130 const std::shared_ptr<CardReader> cardReader,
131 const std::shared_ptr<CalypsoCard> calypsoCard)
132: CardTransactionManagerAdapter(cardReader, calypsoCard, nullptr) {}
133
134const std::shared_ptr<CardReader> CardTransactionManagerAdapter::getCardReader() const
135{
136 return std::dynamic_pointer_cast<CardReader>(mCardReader);
137}
138
139const std::shared_ptr<CalypsoCard> CardTransactionManagerAdapter::getCalypsoCard() const
140{
141 return mCalypsoCard;
142}
143
144const std::shared_ptr<CardSecuritySetting> CardTransactionManagerAdapter::getCardSecuritySetting()
145 const
146{
147 return mCardSecuritySettings;
148}
149
151{
152 return "";
153}
154
155void CardTransactionManagerAdapter::processAtomicOpening(
156 const WriteAccessLevel writeAccessLevel,
157 std::vector<std::shared_ptr<AbstractCardCommand>>& cardCommands)
158{
159 /* This method should be invoked only if no session was previously open */
160 checkSessionNotOpen();
161
162 if (mCardSecuritySettings == nullptr) {
163 throw IllegalStateException("No security settings are available.");
164 }
165
166 const std::vector<uint8_t> sessionTerminalChallenge = getSessionTerminalChallenge();
167
168 /* Card ApduRequestAdapter List to hold Open Secure Session and other optional commands */
169 std::vector<std::shared_ptr<ApduRequestSpi>> cardApduRequests;
170
171 /*
172 * The sfi and record number to be read when the open secure session command is executed.
173 * The default value is 0 (no record to read) but we will optimize the exchanges if a read
174 * record command has been prepared.
175 */
176 uint8_t sfi = 0;
177 uint8_t recordNumber = 0;
178
179 /*
180 * Let's check if we have a read record command at the top of the command list.
181 *
182 * If so, then the command is withdrawn in favour of its equivalent executed at the same
183 * time as the open secure session command.
184 */
185 if (!cardCommands.empty()) {
186 const std::shared_ptr<AbstractCardCommand> cardCommand = cardCommands[0];
187 if (cardCommand->getCommandRef() == CalypsoCardCommand::READ_RECORDS &&
188 std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getReadMode() ==
190 sfi = std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getSfi();
191 recordNumber =
192 std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getFirstRecordNumber();
193 cardCommands.erase(cardCommands.begin());
194 }
195 }
196
197 /* Build the card Open Secure Session command */
198 auto cmdCardOpenSession =
199 std::make_shared<CmdCardOpenSession>(mCalypsoCard,
200 static_cast<uint8_t>(
201 static_cast<int>(writeAccessLevel) + 1),
202 sessionTerminalChallenge,
203 sfi,
204 recordNumber);
205
206 /* Add the resulting ApduRequestAdapter to the card ApduRequestAdapter list */
207 cardApduRequests.push_back(cmdCardOpenSession->getApduRequest());
208
209 /* Add all optional commands to the card ApduRequestAdapter list */
210 Arrays::addAll(cardApduRequests, getApduRequests(cardCommands));
211
212 /*
213 * Create a CardRequest from the ApduRequestAdapter list, card AID as Selector, keep channel
214 * open
215 */
216 auto cardRequest = std::make_shared<CardRequestAdapter>(cardApduRequests, false);
217
218 /* Transmit the commands to the card */
219 const std::shared_ptr<CardResponseApi> cardResponse = safeTransmit(cardRequest,
220 ChannelControl::KEEP_OPEN);
221
222 /* Retrieve and check the ApduResponses */
223 std::vector<std::shared_ptr<ApduResponseApi>> cardApduResponses =
224 cardResponse->getApduResponses();
225
226 /* Do some basic checks */
227 checkCommandsResponsesSynchronization(cardApduRequests.size(), cardApduResponses.size());
228
229 /*
230 * Parse the response to Open Secure Session (the first item of cardApduResponses)
231 * The updateCalypsoCard method fills the CalypsoCard object with the command data.
232 */
233 try {
235 cmdCardOpenSession,
236 cardApduResponses[0],
237 true);
238 } catch (const CardCommandException& e) {
239 throw CardAnomalyException(std::string(CARD_COMMAND_ERROR) +
240 "processing the response to open session: " +
241 e.getCommand().getName(),
242 std::make_shared<CardCommandException>(e));
243 }
244
245 /*
246 * Build the Digest Init command from card Open Session the session challenge is needed for the
247 * SAM digest computation
248 */
249 const std::vector<uint8_t> sessionCardChallenge = cmdCardOpenSession->getCardChallenge();
250
251 /* The card KIF */
252 const std::shared_ptr<uint8_t> cardKif = cmdCardOpenSession->getSelectedKif();
253
254 /* The card KVC, may be null for card Rev 1.0 */
255 const std::shared_ptr<uint8_t> cardKvc = cmdCardOpenSession->getSelectedKvc();
256
257 const std::string logCardKif = cardKif != nullptr ? std::to_string(*cardKif) : "null";
258 const std::string logCardKvc = cardKvc != nullptr ? std::to_string(*cardKvc) : "null";
259 mLogger->debug("processAtomicOpening => opening: CARDCHALLENGE = %, CARDKIF = %, CARDKVC = %\n",
260 ByteArrayUtil::toHex(sessionCardChallenge),
261 logCardKif,
262 logCardKvc);
263
264 const std::shared_ptr<uint8_t> kvc = mSamCommandProcessor->computeKvc(writeAccessLevel, cardKvc);
265 const std::shared_ptr<uint8_t> kif =
266 mSamCommandProcessor->computeKif(writeAccessLevel, cardKif, kvc);
267
268 if (!std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
269 ->isSessionKeyAuthorized(kif, kvc)) {
270 const std::string logKif = kif != nullptr ? std::to_string(*kif) : "null";
271 const std::string logKvc = kvc != nullptr ? std::to_string(*kvc) : "null";
272 throw UnauthorizedKeyException("Unauthorized key error: KIF = " +
273 logKif +
274 ", KVC = " +
275 logKvc);
276 }
277
278 /*
279 * Initialize the digest processor. It will store all digest operations (Digest Init, Digest
280 * Update) until the session closing. At this moment, all SAM Apdu will be processed at
281 * once.
282 */
283 mSamCommandProcessor->initializeDigester(false,
284 false,
285 *kif,
286 *kvc,
287 cardApduResponses[0]->getDataOut());
288
289 /*
290 * Add all commands data to the digest computation. The first command in the list is the
291 * open secure session command. This command is not included in the digest computation, so
292 * we skip it and start the loop at index 1.
293 */
294 if (!cardCommands.empty()) {
295 /* Add requests and responses to the digest processor */
296 mSamCommandProcessor->pushCardExchangedData(cardApduRequests, cardApduResponses, 1);
297 }
298
299 /* Remove Open Secure Session response and create a new CardResponse */
300 cardApduResponses.erase(cardApduResponses.begin());
301
302 /* Update CalypsoCard with the received data */
303 try {
305 cardCommands,
306 cardApduResponses,
307 true);
308 } catch (const CardCommandException& e) {
309 throw CardAnomalyException(CARD_COMMAND_ERROR +
310 "processing the response to open session: " +
311 e.getCommand().getName(),
312 std::make_shared<CardCommandException>(e));
313 }
314
315 mSessionState = SessionState::SESSION_OPEN;
316}
317
319 const uint8_t sfi, const uint8_t counterNumber, const int newValue)
320{
321 std::shared_ptr<int> oldValue;
322
323 const std::shared_ptr<ElementaryFile> ef = mCalypsoCard->getFileBySfi(sfi);
324 if (ef != nullptr) {
325 oldValue = ef->getData()->getContentAsCounterValue(counterNumber);
326 }
327
328 if (oldValue == nullptr) {
329 throw IllegalStateException("The value for counter " + std::to_string(counterNumber) +
330 " in file " + std::to_string(sfi) + " is not available");
331 }
332
333 const int delta = newValue - *oldValue;
334 if (delta > 0) {
335 mLogger->trace("Increment counter % (file %) from % to %\n",
336 counterNumber,
337 sfi,
338 newValue - delta,
339 newValue);
340
341 prepareIncreaseCounter(sfi, counterNumber, delta);
342 } else if (delta < 0) {
343 mLogger->trace("Decrement counter % (file %) from % to %\n",
344 counterNumber,
345 sfi,
346 newValue - delta,
347 newValue);
348
349 prepareDecreaseCounter(sfi, counterNumber, -delta);
350 } else {
351 mLogger->info("The counter % (SFI %) is already set to the desired value %\n",
352 counterNumber,
353 sfi,
354 newValue);
355 }
356
357 return *this;
358}
359
360CardTransactionManager& CardTransactionManagerAdapter::prepareIncreaseOrDecreaseCounters(
361 const bool isDecreaseCommand,
362 const uint8_t sfi,
363 const std::map<const int, const int>& counterNumberToIncDecValueMap)
364{
365 if (mCalypsoCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3 &&
366 mCalypsoCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_2) {
367 throw UnsupportedOperationException("The 'Increase/Decrease Multiple' commands are not " \
368 "available for this card.");
369 }
370
371 Assert::getInstance().isInRange((int) sfi,
374 "sfi")
375 .isInRange(counterNumberToIncDecValueMap.size(),
378 "counterNumberToIncDecValueMap");
379
380 for (const auto& entry : counterNumberToIncDecValueMap) {
381 Assert::getInstance().isInRange(entry.first,
384 "counterNumberToIncDecValueMapKey")
385 .isInRange(entry.second,
388 "counterNumberToIncDecValueMapValue");
389 }
390
391 const int nbCountersPerApdu = mCalypsoCard->getPayloadCapacity() / 4;
392
393 if (static_cast<int>(counterNumberToIncDecValueMap.size()) <= nbCountersPerApdu) {
394 /* Create the command and add it to the list of commands */
395 const std::map<const int, const int> dummy;
396 mCardCommandManager->addRegularCommand(
397 std::make_shared<CmdCardIncreaseOrDecreaseMultiple>(
398 isDecreaseCommand,
399 mCalypsoCard->getCardClass(),
400 sfi,
401 dummy));
402 } else {
403 /*
404 * The number of counters exceeds the payload capacity, let's split into several apdu c
405 * ommands
406 */
407 int i = 0;
408 std::map<const int, const int> map;
409
410 for (const auto& entry : counterNumberToIncDecValueMap) {
411 i++;
412 map.insert({entry.first, entry.second});
413 if (i == nbCountersPerApdu) {
414 mCardCommandManager->addRegularCommand(
415 std::make_shared<CmdCardIncreaseOrDecreaseMultiple>(
416 isDecreaseCommand,
417 mCalypsoCard->getCardClass(),
418 sfi,
419 map));
420 i = 0;
421 map.clear();
422 }
423 }
424
425 if (!map.empty()) {
426 mCardCommandManager->addRegularCommand(
427 std::make_shared<CmdCardIncreaseOrDecreaseMultiple>(isDecreaseCommand,
428 mCalypsoCard->getCardClass(),
429 sfi,
430 map));
431 }
432 }
433
434 return *this;
435}
436
437const std::vector<std::shared_ptr<ApduRequestSpi>> CardTransactionManagerAdapter::getApduRequests(
438 const std::vector<std::shared_ptr<AbstractCardCommand>>& cardCommands)
439{
440 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests;
441
442 if (!cardCommands.empty()) {
443 for (const auto& command : cardCommands) {
444 apduRequests.push_back(command->getApduRequest());
445 }
446 }
447
448 return apduRequests;
449}
450
451void CardTransactionManagerAdapter::processAtomicCardCommands(
452 const std::vector<std::shared_ptr<AbstractCardCommand>> cardCommands,
453 const ChannelControl channelControl)
454{
455 /* Get the card ApduRequestAdapter List */
456 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests = getApduRequests(cardCommands);
457
458 /*
459 * Create a CardRequest from the ApduRequestAdapter list, card AID as Selector, manage the
460 * logical channel according to the channelControl
461 */
462 std::shared_ptr<CardRequestSpi> cardRequest =
463 std::make_shared<CardRequestAdapter>(apduRequests, false);
464
465 /* Transmit the commands to the card */
466 const std::shared_ptr<CardResponseApi> cardResponse = safeTransmit(cardRequest, channelControl);
467
468 /* Retrieve and check the ApduResponses */
469 const std::vector<std::shared_ptr<ApduResponseApi>> cardApduResponses =
470 cardResponse->getApduResponses();
471
472 /* Do some basic checks */
473 checkCommandsResponsesSynchronization(apduRequests.size(), cardApduResponses.size());
474
475 /*
476 * Add all commands data to the digest computation if this method is invoked within a Secure
477 * Session.
478 */
479 if (mSessionState == SessionState::SESSION_OPEN) {
480 mSamCommandProcessor->pushCardExchangedData(apduRequests, cardApduResponses, 0);
481 }
482
483 try {
485 cardCommands,
486 cardResponse->getApduResponses(),
487 mSessionState == SessionState::SESSION_OPEN);
488 } catch (const CardCommandException& e) {
489 throw CardAnomalyException(CARD_COMMAND_ERROR +
490 "processing responses to card commands: " +
491 e.getCommand().getName(),
492 std::make_shared<CardCommandException>(e));
493 }
494}
495
496void CardTransactionManagerAdapter::processAtomicClosing(
497 const std::vector<std::shared_ptr<AbstractCardCommand>>& cardModificationCommands,
498 const std::vector<std::shared_ptr<ApduResponseApi>>& cardAnticipatedResponses,
499 const bool isRatificationMechanismEnabled,
500 const ChannelControl channelControl)
501{
502 checkSessionOpen();
503
504 /* Get the card ApduRequestAdapter List - for the first card exchange */
505 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests =
506 getApduRequests(cardModificationCommands);
507
508 /* Compute "anticipated" Digest Update (for optional cardModificationCommands) */
509 if (!cardModificationCommands.empty() && !apduRequests.empty()) {
510 checkCommandsResponsesSynchronization(apduRequests.size(), cardAnticipatedResponses.size());
511
512 /* Add all commands data to the digest computation: commands and anticipated responses */
513 mSamCommandProcessor->pushCardExchangedData(apduRequests, cardAnticipatedResponses, 0);
514 }
515
516 /*
517 * All SAM digest operations will now run at once.
518 * Get Terminal Signature from the latest response
519 */
520 const std::vector<uint8_t> sessionTerminalSignature = getSessionTerminalSignature();
521
522 /* Build the card Close Session command. The last one for this session */
523 auto cmdCardCloseSession =
524 std::make_shared<CmdCardCloseSession>(mCalypsoCard,
525 !isRatificationMechanismEnabled,
526 sessionTerminalSignature);
527
528 apduRequests.push_back(cmdCardCloseSession->getApduRequest());
529
530 /* Keep the cardsition of the Close Session command in request list */
531 const int closeCommandIndex = static_cast<int>(apduRequests.size()) - 1;
532
533 /* Add the card Ratification command if any */
534 bool ratificationCommandAdded;
535 if (isRatificationMechanismEnabled &&
536 std::dynamic_pointer_cast<CardReader>(mCardReader)->isContactless()) {
537 /*
538 * CL-RAT-CMD.1
539 * CL-RAT-DELAY.1
540 * CL-RAT-NXTCLOSE.1
541 */
542 apduRequests.push_back(
543 CmdCardRatificationBuilder::getApduRequest(mCalypsoCard->getCardClass()));
544 ratificationCommandAdded = true;
545 } else {
546 ratificationCommandAdded = false;
547 }
548
549 /* Transfer card commands */
550 auto cardRequest = std::make_shared<CardRequestAdapter>(apduRequests, false);
551 std::shared_ptr<CardResponseApi> cardResponse;
552
553 try {
554 cardResponse = mCardReader->transmitCardRequest(cardRequest, channelControl);
555 } catch (const CardBrokenCommunicationException& e) {
556 cardResponse = e.getCardResponse();
557
558 /*
559 * The current exception may have been caused by a communication issue with the card
560 * during the ratification command.
561 *
562 * In this case, we do not stop the process and consider the Secure Session close. We'll
563 * check the signature.
564 *
565 * We should have one response less than requests.
566 */
567 if (!ratificationCommandAdded ||
568 cardResponse == nullptr ||
569 cardResponse->getApduResponses().size() != apduRequests.size() - 1) {
570 throw CardIOException(CARD_COMMUNICATION_ERROR + TRANSMITTING_COMMANDS,
571 std::make_shared<CardBrokenCommunicationException>(e));
572 }
573 } catch (const ReaderBrokenCommunicationException& e) {
574 throw CardIOException(CARD_READER_COMMUNICATION_ERROR + TRANSMITTING_COMMANDS,
575 std::make_shared<ReaderBrokenCommunicationException>(e));
576 } catch (const UnexpectedStatusWordException& e) {
577 throw IllegalStateException(UNEXPECTED_EXCEPTION,
578 std::make_shared<UnexpectedStatusWordException>(e));
579 }
580
581 const std::vector<std::shared_ptr<ApduResponseApi>> apduResponses =
582 cardResponse->getApduResponses();
583
584 /*
585 * Check the commands executed before closing the secure session (only responses to these
586 * commands will be taken into account)
587 */
588 try {
590 cardModificationCommands,
591 apduResponses,
592 true);
593 } catch (const CardCommandException& e) {
594 throw CardAnomalyException(CARD_COMMAND_ERROR +
595 "processing of responses preceding the close of the session: " +
596 e.getCommand().getName(),
597 std::make_shared<CardCommandException>(e));
598 }
599
600 /* Check the card's response to Close Secure Session */
601 try {
603 cmdCardCloseSession,
604 apduResponses[closeCommandIndex],
605 true);
606 } catch (const CardSecurityDataException& e) {
607 throw CardCloseSecureSessionException("Invalid card session",
608 std::make_shared<CardSecurityDataException>(e));
609 } catch (const CardCommandException& e) {
610 throw CardAnomalyException(CARD_COMMAND_ERROR +
611 "processing the response to close session: " +
612 e.getCommand().getName(),
613 std::make_shared<CardCommandException>(e));
614 }
615
616 /*
617 * Check the card signature
618 * CL-CSS-MACVERIF.1
619 */
620 checkCardSignature(cmdCardCloseSession->getSignatureLo());
621
622 /*
623 * If necessary, we check the status of the SV after the session has been successfully closed.
624 * CL-SV-POSTPON.1
625 */
626 if (mCardCommandManager->isSvOperationCompleteOneTime()) {
627 checkSvOperationStatus(cmdCardCloseSession->getPostponedData());
628 }
629
630 mSessionState = SessionState::SESSION_CLOSED;
631}
632
633void CardTransactionManagerAdapter::processAtomicClosing(
634 const std::vector<std::shared_ptr<AbstractCardCommand>>& cardCommands,
635 const bool isRatificationMechanismEnabled,
636 const ChannelControl channelControl)
637{
638 const std::vector<std::shared_ptr<ApduResponseApi>> cardAnticipatedResponses =
639 getAnticipatedResponses(cardCommands);
640
641 processAtomicClosing(cardCommands,
642 cardAnticipatedResponses,
643 isRatificationMechanismEnabled,
644 channelControl);
645}
646
647int CardTransactionManagerAdapter::getCounterValue(const uint8_t sfi, const int counter)
648{
649 const std::shared_ptr<ElementaryFile> ef = mCalypsoCard->getFileBySfi(sfi);
650 if (ef != nullptr) {
651 const std::shared_ptr<int> counterValue = ef->getData()->getContentAsCounterValue(counter);
652 if (counterValue != nullptr) {
653 return *counterValue;
654 }
655 }
656
657 std::stringstream ss;
658 ss << "Anticipated response. Unable to determine anticipated value of counter "
659 << counter
660 << " in EF sfi "
661 << sfi;
662 throw IllegalStateException(ss.str());
663}
664
665const std::map<const int, const int> CardTransactionManagerAdapter::getCounterValues(
666 const uint8_t sfi, const std::vector<int>& counters)
667{
668 const std::shared_ptr<ElementaryFile> ef = mCalypsoCard->getFileBySfi(sfi);
669 if (ef != nullptr) {
670 const std::map<const int, const int> allCountersValue = ef->getData()->getAllCountersValue();
671
672 if (Arrays::containsAll(MapUtils::getKeySet(allCountersValue), counters)) {
673 return allCountersValue;
674 }
675 }
676
677 std::stringstream ss;
678 ss << "Anticipated response. Unable to determine anticipated value of counters in EF sfi "
679 << sfi;
680 throw IllegalStateException(ss.str());
681}
682
683const std::shared_ptr<ApduResponseApi> CardTransactionManagerAdapter::createIncreaseDecreaseResponse(
684 const bool isDecreaseCommand, const int currentCounterValue, const int incDecValue)
685{
686 const int newValue = isDecreaseCommand ? currentCounterValue - incDecValue :
687 currentCounterValue + incDecValue;
688
689 /* Response = NNNNNN9000 */
690 std::vector<uint8_t> response(5);
691 response[0] = static_cast<uint8_t>((newValue & 0x00FF0000) >> 16);
692 response[1] = static_cast<uint8_t>((newValue & 0x0000FF00) >> 8);
693 response[2] = static_cast<uint8_t>(newValue & 0x000000FF);
694 response[3] = 0x90;
695 response[4] = 0x00;
696
697 return std::make_shared<ApduResponseAdapter>(response);
698}
699
700const std::shared_ptr<ApduResponseApi>
701 CardTransactionManagerAdapter::createIncreaseDecreaseMultipleResponse(
702 const bool isDecreaseCommand,
703 const std::map<const int, const int>& counterNumberToCurrentValueMap,
704 const std::map<const int, const int>& counterNumberToIncDecValueMap)
705{
706 /* Response = CCVVVVVV..CCVVVVVV9000 */
707 std::vector<uint8_t> response(2 + counterNumberToIncDecValueMap.size() * 4);
708 int index = 0;
709
710 for (const auto& entry : counterNumberToIncDecValueMap) {
711 response[index] = static_cast<uint8_t>(entry.first);
712 int newCounterValue;
713 if (isDecreaseCommand) {
714 const auto it = counterNumberToCurrentValueMap.find(entry.first);
715 newCounterValue = it->second - entry.second;
716 } else {
717 const auto it = counterNumberToCurrentValueMap.find(entry.first);
718 newCounterValue = it->second + entry.second;
719 }
720
721 response[index + 1] = static_cast<uint8_t>((newCounterValue & 0x00FF0000) >> 16);
722 response[index + 2] = static_cast<uint8_t>((newCounterValue & 0x0000FF00) >> 8);
723 response[index + 3] = static_cast<uint8_t>(newCounterValue & 0x000000FF);
724 index += 4;
725 }
726
727 response[index] = 0x90;
728 response[index + 1] = 0x00;
729
730 return std::make_shared<ApduResponseAdapter>(response);
731}
732
733const std::vector<std::shared_ptr<ApduResponseApi>>
734 CardTransactionManagerAdapter::getAnticipatedResponses(
735 const std::vector<std::shared_ptr<AbstractCardCommand>>& cardCommands)
736{
737 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses;
738
739 if (!cardCommands.empty()) {
740 for (const auto& command : cardCommands) {
741 if (command->getCommandRef() == CalypsoCardCommand::INCREASE ||
742 command->getCommandRef() == CalypsoCardCommand::DECREASE) {
743 auto incdec = std::dynamic_pointer_cast<CmdCardIncreaseOrDecrease>(command);
744 const uint8_t sfi = incdec->getSfi();
745 const int counter = incdec->getCounterNumber();
746
747 apduResponses.push_back(
748 createIncreaseDecreaseResponse(
749 command->getCommandRef() == CalypsoCardCommand::DECREASE,
750 getCounterValue(sfi, counter),
751 incdec->getIncDecValue()));
752
753 } else if (command->getCommandRef() == CalypsoCardCommand::INCREASE_MULTIPLE ||
754 command->getCommandRef() == CalypsoCardCommand::DECREASE_MULTIPLE) {
755 auto incdec = std::dynamic_pointer_cast<CmdCardIncreaseOrDecreaseMultiple>(command);
756 const uint8_t sfi = incdec->getSfi();
757 const std::map<const int, const int> counterNumberToIncDecValueMap =
758 incdec->getCounterNumberToIncDecValueMap();
759
760 apduResponses.push_back(
761 createIncreaseDecreaseMultipleResponse(
762 command->getCommandRef() == CalypsoCardCommand::DECREASE_MULTIPLE,
763 getCounterValues(sfi, MapUtils::getKeySet(counterNumberToIncDecValueMap)),
764 counterNumberToIncDecValueMap));
765
766 } else if (command->getCommandRef() == CalypsoCardCommand::SV_RELOAD ||
767 command->getCommandRef() == CalypsoCardCommand::SV_DEBIT ||
768 command->getCommandRef() == CalypsoCardCommand::SV_UNDEBIT) {
769 apduResponses.push_back(RESPONSE_OK_POSTPONED);
770
771 } else {
772 /* Append/Update/Write Record: response = 9000 */
773 apduResponses.push_back(RESPONSE_OK);
774 }
775 }
776 }
777
778 return apduResponses;
779}
780
782 const WriteAccessLevel writeAccessLevel)
783{
784 /* CL-KEY-INDEXPO.1 */
785 mCurrentWriteAccessLevel = writeAccessLevel;
786
787 /* Create a sublist of AbstractCardCommand to be sent atomically */
788 std::vector<std::shared_ptr<AbstractCardCommand>> cardAtomicCommands;
789
790 std::atomic<int> neededSessionBufferSpace;
791 std::atomic<bool> overflow;
792
793 for (const auto& command : mCardCommandManager->getCardCommands()) {
794 /*
795 * Check if the command is a modifying one and get it status (overflow yes/no,
796 * neededSessionBufferSpace). If the command overflows the session buffer in atomic
797 * modification mode, an exception is raised.
798 */
799 if (checkModifyingCommand(command, overflow, neededSessionBufferSpace)) {
800 if (overflow) {
801 /* Open the session with the current commands */
802 processAtomicOpening(mCurrentWriteAccessLevel, cardAtomicCommands);
803
804 /*
805 * Closes the session, resets the modifications buffer counters for the next
806 * round.
807 */
808 processAtomicClosing(std::vector<std::shared_ptr<AbstractCardCommand>>(),
809 false,
810 ChannelControl::KEEP_OPEN);
811 resetModificationsBufferCounter();
812
813 /*
814 *Clear the list and add the command that did not fit in the card modifications
815 * buffer. We also update the usage counter without checking the result.
816 */
817 cardAtomicCommands.clear();
818 cardAtomicCommands.push_back(command);
819
820 /* Just update modifications buffer usage counter, ignore result (always false) */
821 isSessionBufferOverflowed(neededSessionBufferSpace);
822 } else {
823 /* The command fits in the card modifications buffer, just add it to the list */
824 cardAtomicCommands.push_back(command);
825 }
826 } else {
827 /* This command does not affect the card modifications buffer */
828 cardAtomicCommands.push_back(command);
829 }
830 }
831
832 processAtomicOpening(mCurrentWriteAccessLevel, cardAtomicCommands);
833
834 /* Sets the flag indicating that the commands have been executed */
835 mCardCommandManager->notifyCommandsProcessed();
836
837 return *this;
838}
839
840void CardTransactionManagerAdapter::processCardCommandsOutOfSession(
841 const ChannelControl channelControl)
842{
843 /* Card commands sent outside a Secure Session. No modifications buffer limitation */
844 processAtomicCardCommands(mCardCommandManager->getCardCommands(), channelControl);
845
846 /* Sets the flag indicating that the commands have been executed */
847 mCardCommandManager->notifyCommandsProcessed();
848
849 /* If an SV transaction was performed, we check the signature returned by the card here */
850 if (mCardCommandManager->isSvOperationCompleteOneTime()) {
851 try {
852 mSamCommandProcessor->checkSvStatus(mCalypsoCard->getSvOperationSignature());
853 } catch (const CalypsoSamSecurityDataException& e) {
854 throw SvAuthenticationException("The checking of the SV operation by the SAM has " \
855 "failed.",
856 std::make_shared<CalypsoSamSecurityDataException>(e));
857 } catch (const CalypsoSamCommandException& e) {
858 throw SamAnomalyException(SAM_COMMAND_ERROR +
859 "checking the SV operation: " +
860 e.getCommand().getName(),
861 std::make_shared<CalypsoSamCommandException>(e));
862 } catch (const ReaderBrokenCommunicationException& e) {
863 throw SvAuthenticationException(
864 SAM_READER_COMMUNICATION_ERROR + CHECKING_THE_SV_OPERATION,
865 std::make_shared<ReaderBrokenCommunicationException>(e));
866 } catch (const CardBrokenCommunicationException& e) {
867 throw SvAuthenticationException(SAM_COMMUNICATION_ERROR + CHECKING_THE_SV_OPERATION,
868 std::make_shared<CardBrokenCommunicationException>(e));
869 }
870 }
871}
872
873void CardTransactionManagerAdapter::processCardCommandsInSession()
874{
875 /* A session is open, we have to care about the card modifications buffer */
876 std::vector<std::shared_ptr<AbstractCardCommand>> cardAtomicCommands;
877
878 std::atomic<int> neededSessionBufferSpace;
879 std::atomic<bool> overflow;
880
881 for (const auto& command : mCardCommandManager->getCardCommands()) {
882 /*
883 * Check if the command is a modifying one and get it status (overflow yes/no,
884 * neededSessionBufferSpace)
885 * if the command overflows the session buffer in atomic modification mode, an exception
886 * is raised.
887 */
888 if (checkModifyingCommand(command, overflow, neededSessionBufferSpace)) {
889 if (overflow) {
890 /*
891 * The current command would overflow the modifications buffer in the card. We
892 * send the current commands and update the command list. The command Iterator is
893 * kept all along the process.
894 */
895 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
896
897 /* Close the session and reset the modifications buffer counters for the next round */
898 processAtomicClosing(std::vector<std::shared_ptr<AbstractCardCommand>>(),
899 false,
900 ChannelControl::KEEP_OPEN);
901 resetModificationsBufferCounter();
902
903 /* We reopen a new session for the remaining commands to be sent */
904 std::vector<std::shared_ptr<AbstractCardCommand>> dummy;
905 processAtomicOpening(mCurrentWriteAccessLevel, dummy);
906
907 /*
908 * Clear the list and add the command that did not fit in the card modifications
909 * buffer. We also update the usage counter without checking the result.
910 */
911 cardAtomicCommands.clear();
912 cardAtomicCommands.push_back(command);
913
914 /* Just update modifications buffer usage counter, ignore result (always false) */
915 isSessionBufferOverflowed(neededSessionBufferSpace);
916 } else {
917 /* The command fits in the card modifications buffer, just add it to the list */
918 cardAtomicCommands.push_back(command);
919 }
920 } else {
921 /* This command does not affect the card modifications buffer */
922 cardAtomicCommands.push_back(command);
923 }
924 }
925
926 if (!cardAtomicCommands.empty()) {
927 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
928 }
929
930 /* Sets the flag indicating that the commands have been executed */
931 mCardCommandManager->notifyCommandsProcessed();
932}
933
935{
936 if (mSessionState == SessionState::SESSION_OPEN) {
937 processCardCommandsInSession();
938 } else {
939 processCardCommandsOutOfSession(mChannelControl);
940 }
941
942 return *this;
943}
944
946{
947 checkSessionOpen();
948
949 bool atLeastOneReadCommand = false;
950 bool sessionPreviouslyClosed = false;
951
952 std::atomic<int> neededSessionBufferSpace;
953 std::atomic<bool> overflow;
954
955 std::vector<std::shared_ptr<AbstractCardCommand>> cardAtomicCommands;
956
957 for (const auto& command : mCardCommandManager->getCardCommands()) {
958 /*
959 * Check if the command is a modifying one and get it status (overflow yes/no,
960 * neededSessionBufferSpace). Iif the command overflows the session buffer in atomic
961 * modification mode, an exception is raised.
962 */
963 if (checkModifyingCommand(command, overflow, neededSessionBufferSpace)) {
964 if (overflow) {
965 /*
966 * Reopen a session with the same access level if it was previously closed in
967 * this current processClosing
968 */
969 if (sessionPreviouslyClosed) {
970
971 std::vector<std::shared_ptr<AbstractCardCommand>> dummy;
972 processAtomicOpening(mCurrentWriteAccessLevel, dummy);
973 }
974
975 /*
976 * If at least one non-modifying was prepared, we use processAtomicCardCommands
977 * instead of processAtomicClosing to send the list
978 */
979 if (atLeastOneReadCommand) {
980 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
981
982 /* Clear the list of commands sent */
983 cardAtomicCommands.clear();
984 processAtomicClosing(cardAtomicCommands, false, ChannelControl::KEEP_OPEN);
985 resetModificationsBufferCounter();
986 sessionPreviouslyClosed = true;
987 atLeastOneReadCommand = false;
988 } else {
989 /* All commands in the list are 'modifying the card' */
990 processAtomicClosing(cardAtomicCommands, false, ChannelControl::KEEP_OPEN);
991
992 /* Clear the list of commands sent */
993 cardAtomicCommands.clear();
994 resetModificationsBufferCounter();
995 sessionPreviouslyClosed = true;
996 }
997
998 /*
999 * Add the command that did not fit in the card modifications
1000 * buffer. We also update the usage counter without checking the result.
1001 */
1002 cardAtomicCommands.push_back(command);
1003
1004 /* Just update modifications buffer usage counter, ignore result (always false) */
1005 isSessionBufferOverflowed(neededSessionBufferSpace);
1006 } else {
1007 /* The command fits in the card modifications buffer, just add it to the list */
1008 cardAtomicCommands.push_back(command);
1009 }
1010 } else {
1011 /* This command does not affect the card modifications buffer */
1012 cardAtomicCommands.push_back(command);
1013 atLeastOneReadCommand = true;
1014 }
1015 }
1016
1017 if (sessionPreviouslyClosed) {
1018 /* Reopen a session if necessary */
1019 std::vector<std::shared_ptr<AbstractCardCommand>> dummy;
1020 processAtomicOpening(mCurrentWriteAccessLevel, dummy);
1021 }
1022
1023 if (atLeastOneReadCommand) {
1024 /* Execute the command */
1025 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
1026 cardAtomicCommands.clear();
1027 }
1028
1029 /* Finally, close the session as requested */
1030 processAtomicClosing(cardAtomicCommands,
1031 std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
1032 ->isRatificationMechanismEnabled(),
1033 mChannelControl);
1034
1035 /* Sets the flag indicating that the commands have been executed */
1036 mCardCommandManager->notifyCommandsProcessed();
1037
1038 return *this;
1039}
1040
1042{
1043 checkSessionOpen();
1044
1045 /* Card ApduRequestAdapter List to hold Close Secure Session command */
1046 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests;
1047
1048 /* Build the card Close Session command (in "abort" mode since no signature is provided) */
1049 auto cmdCardCloseSession = std::make_shared<CmdCardCloseSession>(mCalypsoCard);
1050
1051 apduRequests.push_back(cmdCardCloseSession->getApduRequest());
1052
1053 /* Transfer card commands */
1054 std::shared_ptr<CardRequestSpi> cardRequest =
1055 std::make_shared<CardRequestAdapter>(apduRequests, false);
1056
1057 const std::shared_ptr<CardResponseApi> cardResponse = safeTransmit(cardRequest,
1058 mChannelControl);
1059
1060 try {
1061 cmdCardCloseSession->setApduResponse(cardResponse->getApduResponses()[0]).checkStatus();
1062 } catch (const CardCommandException& e) {
1063 throw CardAnomalyException(CARD_COMMAND_ERROR +
1064 "processing the response to close session: " +
1065 e.getCommand().getName(),
1066 std::make_shared<CardCommandException>(e));
1067 }
1068
1069 /* Sets the flag indicating that the commands have been executed */
1070 mCardCommandManager->notifyCommandsProcessed();
1071
1072 /*
1073 * Session is now considered closed regardless the previous state or the result of the abort
1074 * session command sent to the card.
1075 */
1076 mSessionState = SessionState::SESSION_CLOSED;
1077
1078 return *this;
1079}
1080
1082 const std::vector<uint8_t>& pin)
1083{
1084 Assert::getInstance().isEqual(pin.size(), CalypsoCardConstant::PIN_LENGTH, "PIN length");
1085
1086 if (!mCalypsoCard->isPinFeatureAvailable()) {
1087 throw UnsupportedOperationException(PIN_NOT_AVAILABLE_ERROR);
1088 }
1089
1090 if (mCardCommandManager->hasCommands()) {
1091 throw IllegalStateException("No commands should have been prepared prior to a PIN " \
1092 "submission.");
1093 }
1094
1095 /* CL-PIN-PENCRYPT.1 */
1096 if (mCardSecuritySettings != nullptr &&
1097 !std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
1098 ->isPinPlainTransmissionEnabled()) {
1099
1100 /* CL-PIN-GETCHAL.1 */
1101 mCardCommandManager->addRegularCommand(
1102 std::make_shared<CmdCardGetChallenge>(mCalypsoCard->getCardClass()));
1103
1104 /* Transmit and receive data with the card */
1105 processAtomicCardCommands(mCardCommandManager->getCardCommands(),
1106 ChannelControl::KEEP_OPEN);
1107
1108 /* Sets the flag indicating that the commands have been executed */
1109 mCardCommandManager->notifyCommandsProcessed();
1110
1111 /* Get the encrypted PIN with the help of the SAM */
1112 std::vector<uint8_t> cipheredPin;
1113 try {
1114 cipheredPin = mSamCommandProcessor->getCipheredPinData(mCalypsoCard->getCardChallenge(),
1115 pin,
1116 std::vector<uint8_t>());
1117 } catch (const CalypsoSamCommandException& e) {
1118 throw SamAnomalyException(SAM_COMMAND_ERROR +
1119 "generating of the PIN ciphered data: " +
1120 e.getCommand().getName(),
1121 std::make_shared<CalypsoSamCommandException>(e));
1122 } catch (const ReaderBrokenCommunicationException& e) {
1123 throw SamIOException(SAM_READER_COMMUNICATION_ERROR +
1124 GENERATING_OF_THE_PIN_CIPHERED_DATA_ERROR,
1125 std::make_shared<ReaderBrokenCommunicationException>(e));
1126 } catch (const CardBrokenCommunicationException& e) {
1127 throw SamIOException(SAM_COMMUNICATION_ERROR +
1128 GENERATING_OF_THE_PIN_CIPHERED_DATA_ERROR,
1129 std::make_shared<CardBrokenCommunicationException>(e));
1130 }
1131
1132 mCardCommandManager->addRegularCommand(
1133 std::make_shared<CmdCardVerifyPin>(mCalypsoCard->getCardClass(), true, cipheredPin));
1134 } else {
1135 mCardCommandManager->addRegularCommand(
1136 std::make_shared<CmdCardVerifyPin>(mCalypsoCard->getCardClass(), false, pin));
1137 }
1138
1139 /* Transmit and receive data with the card */
1140 processAtomicCardCommands(mCardCommandManager->getCardCommands(), mChannelControl);
1141
1142 /* Sets the flag indicating that the commands have been executed */
1143 mCardCommandManager->notifyCommandsProcessed();
1144
1145 return *this;
1146}
1147
1149 const std::vector<uint8_t>& newPin)
1150{
1151
1152 Assert::getInstance().isEqual(newPin.size(), CalypsoCardConstant::PIN_LENGTH, "PIN length");
1153
1154 if (!mCalypsoCard->isPinFeatureAvailable()) {
1155 throw UnsupportedOperationException(PIN_NOT_AVAILABLE_ERROR);
1156 }
1157
1158 if (mSessionState == SessionState::SESSION_OPEN) {
1159 throw IllegalStateException("'Change PIN' not allowed when a secure session is open.");
1160 }
1161
1162 /* CL-PIN-MENCRYPT.1 */
1163 if (std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
1164 ->isPinPlainTransmissionEnabled()) {
1165 /* Transmission in plain mode */
1166 if (mCalypsoCard->getPinAttemptRemaining() >= 0) {
1167 mCardCommandManager->addRegularCommand(
1168 std::make_shared<CmdCardChangePin>(mCalypsoCard->getCardClass(), newPin));
1169 }
1170 } else {
1171 /* CL-PIN-GETCHAL.1 */
1172 mCardCommandManager->addRegularCommand(
1173 std::make_shared<CmdCardGetChallenge>(mCalypsoCard->getCardClass()));
1174
1175 /* Transmit and receive data with the card */
1176 processAtomicCardCommands(mCardCommandManager->getCardCommands(),
1177 ChannelControl::KEEP_OPEN);
1178
1179 /* Sets the flag indicating that the commands have been executed */
1180 mCardCommandManager->notifyCommandsProcessed();
1181
1182 /* Get the encrypted PIN with the help of the SAM */
1183 std::vector<uint8_t> newPinData;
1184 std::vector<uint8_t> currentPin(4); /* All zeros as required */
1185 try {
1186 newPinData =
1187 mSamCommandProcessor->getCipheredPinData(
1188 mCalypsoCard->getCardChallenge(), currentPin, newPin);
1189 } catch (const CalypsoSamCommandException& e) {
1190 throw SamAnomalyException(SAM_COMMAND_ERROR +
1191 "generating of the PIN ciphered data: " +
1192 e.getCommand().getName(),
1193 std::make_shared<CalypsoSamCommandException>(e));
1194 } catch (const ReaderBrokenCommunicationException& e) {
1195 throw SamIOException(SAM_READER_COMMUNICATION_ERROR +
1196 GENERATING_OF_THE_PIN_CIPHERED_DATA_ERROR,
1197 std::make_shared<ReaderBrokenCommunicationException>(e));
1198 } catch (const CardBrokenCommunicationException& e) {
1199 throw SamIOException(SAM_COMMUNICATION_ERROR +
1200 GENERATING_OF_THE_PIN_CIPHERED_DATA_ERROR,
1201 std::make_shared<CardBrokenCommunicationException>(e));
1202 }
1203
1204 mCardCommandManager->addRegularCommand(
1205 std::make_shared<CmdCardChangePin>(mCalypsoCard->getCardClass(), newPinData));
1206 }
1207
1208 /* Transmit and receive data with the card */
1209 processAtomicCardCommands(mCardCommandManager->getCardCommands(), mChannelControl);
1210
1211 /* Sets the flag indicating that the commands have been executed */
1212 mCardCommandManager->notifyCommandsProcessed();
1213
1214 return *this;
1215}
1216
1217CardTransactionManager& CardTransactionManagerAdapter::processChangeKey(const uint8_t keyIndex,
1218 const uint8_t newKif,
1219 const uint8_t newKvc,
1220 const uint8_t issuerKif,
1221 const uint8_t issuerKvc)
1222{
1223 if (mCalypsoCard->getProductType() == CalypsoCard::ProductType::BASIC) {
1224 throw UnsupportedOperationException("The 'Change Key' command is not available for this " \
1225 "card.");
1226 }
1227
1228 if (mSessionState == SessionState::SESSION_OPEN) {
1229 throw IllegalStateException("'Change Key' not allowed when a secure session is open.");
1230 }
1231
1232 Assert::getInstance().isInRange(keyIndex, 1, 3, "keyIndex");
1233
1234 /* CL-KEY-CHANGE.1 */
1235 mCardCommandManager->addRegularCommand(
1236 std::make_shared<CmdCardGetChallenge>(mCalypsoCard->getCardClass()));
1237
1238 /* Transmit and receive data with the card */
1239 processAtomicCardCommands(mCardCommandManager->getCardCommands(), ChannelControl::KEEP_OPEN);
1240
1241 /* Sets the flag indicating that the commands have been executed */
1242 mCardCommandManager->notifyCommandsProcessed();
1243
1244 /* Get the encrypted key with the help of the SAM */
1245 try {
1246 const std::vector<uint8_t> encryptedKey =
1247 mSamCommandProcessor->getEncryptedKey(
1248 mCalypsoCard->getCardChallenge(), issuerKif, issuerKvc, newKif, newKvc);
1249 mCardCommandManager->addRegularCommand(
1250 std::make_shared<CmdCardChangeKey>(mCalypsoCard->getCardClass(),
1251 keyIndex,
1252 encryptedKey));
1253 } catch (const CalypsoSamCommandException& e) {
1254 throw SamAnomalyException(SAM_COMMAND_ERROR +
1255 "generating the encrypted key: " +
1256 e.getCommand().getName(),
1257 std::make_shared<CalypsoSamCommandException>(e));
1258 } catch (const ReaderBrokenCommunicationException& e) {
1259 throw SamIOException(SAM_READER_COMMUNICATION_ERROR +
1260 GENERATING_OF_THE_KEY_CIPHERED_DATA_ERROR,
1261 std::make_shared<ReaderBrokenCommunicationException>(e));
1262 } catch (const CardBrokenCommunicationException& e) {
1263 throw SamIOException(SAM_COMMUNICATION_ERROR + GENERATING_OF_THE_KEY_CIPHERED_DATA_ERROR,
1264 std::make_shared<CardBrokenCommunicationException>(e));
1265 }
1266
1267 /* Transmit and receive data with the card */
1268 processAtomicCardCommands(mCardCommandManager->getCardCommands(), mChannelControl);
1269
1270 /* Sets the flag indicating that the commands have been executed */
1271 mCardCommandManager->notifyCommandsProcessed();
1272
1273 return *this;
1274}
1275
1276const std::shared_ptr<CardResponseApi> CardTransactionManagerAdapter::safeTransmit(
1277 const std::shared_ptr<CardRequestSpi> cardRequest, const ChannelControl channelControl)
1278{
1279 try {
1280 return mCardReader->transmitCardRequest(cardRequest, channelControl);
1281 } catch (const ReaderBrokenCommunicationException& e) {
1282 throw CardIOException(CARD_READER_COMMUNICATION_ERROR + TRANSMITTING_COMMANDS,
1283 std::make_shared<ReaderBrokenCommunicationException>(e));
1284 } catch (const CardBrokenCommunicationException& e) {
1285 throw CardIOException(CARD_COMMUNICATION_ERROR + TRANSMITTING_COMMANDS,
1286 std::make_shared<CardBrokenCommunicationException>(e));
1287 } catch (const UnexpectedStatusWordException& e) {
1288 throw IllegalStateException(UNEXPECTED_EXCEPTION,
1289 std::make_shared<UnexpectedStatusWordException>(e));
1290 }
1291}
1292
1293const std::vector<uint8_t> CardTransactionManagerAdapter::getSessionTerminalChallenge()
1294{
1295 std::vector<uint8_t> sessionTerminalChallenge;
1296
1297 try {
1298 sessionTerminalChallenge = mSamCommandProcessor->getSessionTerminalChallenge();
1299 } catch (const CalypsoSamCommandException& e) {
1300 throw SamAnomalyException(SAM_COMMAND_ERROR +
1301 "getting the terminal challenge: " +
1302 e.getCommand().getName(),
1303 std::make_shared<CalypsoSamCommandException>(e));
1304 } catch (const ReaderBrokenCommunicationException& e) {
1305 throw SamIOException(SAM_READER_COMMUNICATION_ERROR + "getting the terminal challenge.",
1306 std::make_shared<ReaderBrokenCommunicationException>(e));
1307 } catch (const CardBrokenCommunicationException& e) {
1308 throw SamIOException(SAM_COMMUNICATION_ERROR + "getting terminal challenge.",
1309 std::make_shared<CardBrokenCommunicationException>(e));
1310 }
1311
1312 return sessionTerminalChallenge;
1313}
1314
1315const std::vector<uint8_t> CardTransactionManagerAdapter::getSessionTerminalSignature()
1316{
1317 std::vector<uint8_t> sessionTerminalSignature;
1318
1319 try {
1320 sessionTerminalSignature = mSamCommandProcessor->getTerminalSignature();
1321 } catch (const CalypsoSamCommandException& e) {
1322 throw SamAnomalyException(SAM_COMMAND_ERROR +
1323 "getting the terminal signature: " +
1324 e.getCommand().getName(),
1325 std::make_shared<CalypsoSamCommandException>(e));
1326 } catch (const CardBrokenCommunicationException& e) {
1327 throw SamIOException(SAM_COMMUNICATION_ERROR + "getting the terminal signature.",
1328 std::make_shared<CardBrokenCommunicationException>(e));
1329 } catch (const ReaderBrokenCommunicationException& e) {
1330 throw SamIOException(SAM_READER_COMMUNICATION_ERROR + "getting the terminal signature.",
1331 std::make_shared<ReaderBrokenCommunicationException>(e));
1332 }
1333
1334 return sessionTerminalSignature;
1335}
1336
1337void CardTransactionManagerAdapter::checkCardSignature(const std::vector<uint8_t>& cardSignature)
1338{
1339 try {
1340 mSamCommandProcessor->authenticateCardSignature(cardSignature);
1341 } catch (const CalypsoSamSecurityDataException& e) {
1342 throw SessionAuthenticationException("The authentication of the card by the SAM has " \
1343 "failed.",
1344 std::make_shared<CalypsoSamSecurityDataException>(e));
1345 } catch (const CalypsoSamCommandException& e) {
1346 throw SamAnomalyException(SAM_COMMAND_ERROR +
1347 "authenticating the card signature: " +
1348 e.getCommand().getName(),
1349 std::make_shared<CalypsoSamCommandException>(e));
1350 } catch (const ReaderBrokenCommunicationException& e) {
1351 throw SamIOException(SAM_READER_COMMUNICATION_ERROR +
1352 "authenticating the card signature.",
1353 std::make_shared<ReaderBrokenCommunicationException>(e));
1354 } catch (const CardBrokenCommunicationException& e) {
1355 throw SamIOException(SAM_COMMUNICATION_ERROR + "authenticating the card signature.",
1356 std::make_shared<CardBrokenCommunicationException>(e));
1357 }
1358}
1359
1360void CardTransactionManagerAdapter::checkSvOperationStatus(
1361 const std::vector<uint8_t>& cardPostponedData)
1362{
1363 try {
1364 mSamCommandProcessor->checkSvStatus(cardPostponedData);
1365 } catch (const CalypsoSamSecurityDataException& e) {
1366 throw SvAuthenticationException("The checking of the SV operation by the SAM has failed.",
1367 std::make_shared<CalypsoSamSecurityDataException>(e));
1368 } catch (const CalypsoSamCommandException& e) {
1369 throw SamAnomalyException(SAM_COMMAND_ERROR +
1370 "checking the SV operation: " +
1371 e.getCommand().getName(),
1372 std::make_shared<CalypsoSamCommandException>(e));
1373 } catch (const ReaderBrokenCommunicationException& e) {
1374 throw SamIOException(SAM_READER_COMMUNICATION_ERROR + CHECKING_THE_SV_OPERATION,
1375 std::make_shared<ReaderBrokenCommunicationException>(e));
1376 } catch (const CardBrokenCommunicationException& e) {
1377 throw SamIOException(SAM_COMMUNICATION_ERROR + CHECKING_THE_SV_OPERATION,
1378 std::make_shared<CardBrokenCommunicationException>(e));
1379 }
1380}
1381
1382void CardTransactionManagerAdapter::checkSessionOpen()
1383{
1384 if (mSessionState != SessionState::SESSION_OPEN) {
1385 std::stringstream ss;
1386 ss << "Bad session state. Current: " << mSessionState
1387 << ", expected: " << SessionState::SESSION_OPEN;
1388 throw IllegalStateException(ss.str());
1389 }
1390}
1391
1392void CardTransactionManagerAdapter::checkSessionNotOpen()
1393{
1394 if (mSessionState == SessionState::SESSION_OPEN) {
1395 std::stringstream ss;
1396 ss << "Bad session state. Current: " << mSessionState << ", expected: not open";
1397 throw IllegalStateException(ss.str());
1398 }
1399}
1400
1401void CardTransactionManagerAdapter::checkCommandsResponsesSynchronization(
1402 const size_t commandsNumber, const size_t responsesNumber)
1403{
1404 if (commandsNumber != responsesNumber) {
1405 throw DesynchronizedExchangesException("The number of commands/responses does not match: " \
1406 "cmd=" +
1407 std::to_string(commandsNumber) +
1408 ", resp=" +
1409 std::to_string(responsesNumber));
1410 }
1411}
1412
1413bool CardTransactionManagerAdapter::checkModifyingCommand(
1414 const std::shared_ptr<AbstractCardCommand> command,
1415 std::atomic<bool>& overflow,
1416 std::atomic<int>& neededSessionBufferSpace)
1417{
1418 if (command->isSessionBufferUsed()) {
1419 /* This command affects the card modifications buffer */
1420 neededSessionBufferSpace = static_cast<int>(command->getApduRequest()->getApdu().size()) +
1421 SESSION_BUFFER_CMD_ADDITIONAL_COST -
1422 APDU_HEADER_LENGTH;
1423
1424 if (isSessionBufferOverflowed(neededSessionBufferSpace)) {
1425 /*
1426 * Raise an exception if in atomic mode
1427 * CL-CSS-REQUEST.1
1428 * CL-CSS-SMEXCEED.1
1429 * CL-CSS-INFOCSS.1
1430 */
1431 if (!std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
1432 ->isMultipleSessionEnabled()) {
1433 throw AtomicTransactionException("ATOMIC mode error! This command would overflow " \
1434 "the card modifications buffer: " +
1435 command->getName());
1436 }
1437 overflow = true;
1438 } else {
1439 overflow = false;
1440 }
1441
1442 return true;
1443 } else {
1444 return false;
1445 }
1446}
1447
1448bool CardTransactionManagerAdapter::isSessionBufferOverflowed(const int sessionBufferSizeConsumed)
1449{
1450 bool isSessionBufferFull = false;
1451
1452 if (mCalypsoCard->isModificationsCounterInBytes()) {
1453 if (mModificationsCounter - sessionBufferSizeConsumed >= 0) {
1454 mModificationsCounter -= sessionBufferSizeConsumed;
1455 } else {
1456 mLogger->debug("Modifications buffer overflow! BYTESMODE, CURRENTCOUNTER = %, " \
1457 "REQUIREMENT = %\n",
1458 mModificationsCounter,
1459 sessionBufferSizeConsumed);
1460
1461 isSessionBufferFull = true;
1462 }
1463 } else {
1464 if (mModificationsCounter > 0) {
1465 mModificationsCounter--;
1466 } else {
1467 mLogger->debug("Modifications buffer overflow! COMMANDSMODE, CURRENTCOUNTER = %, " \
1468 "REQUIREMENT = %\n",
1469 mModificationsCounter,
1470 1);
1471
1472 isSessionBufferFull = true;
1473 }
1474 }
1475
1476 return isSessionBufferFull;
1477}
1478
1479
1480void CardTransactionManagerAdapter::resetModificationsBufferCounter()
1481{
1482 mLogger->trace("Modifications buffer counter reset: PREVIOUSVALUE = %, NEWVALUE = %\n",
1483 mModificationsCounter,
1484 mCalypsoCard->getModificationsCounter());
1485
1486 mModificationsCounter = mCalypsoCard->getModificationsCounter();
1487}
1488
1490{
1491 mChannelControl = ChannelControl::CLOSE_AFTER;
1492
1493 return *this;
1494}
1495
1497 const std::vector<uint8_t>& lid)
1498{
1499 Assert::getInstance().isEqual(lid.size(), 2, "lid length");
1500
1501 return prepareSelectFile(static_cast<uint16_t>(ByteArrayUtil::twoBytesToInt(lid, 0)));
1502}
1503
1504CardTransactionManager& CardTransactionManagerAdapter::prepareSelectFile(const uint16_t lid)
1505{
1506 mCardCommandManager->addRegularCommand(
1507 std::make_shared<CmdCardSelectFile>(mCalypsoCard->getCardClass(),
1508 mCalypsoCard->getProductType(),
1509 lid));
1510
1511 return *this;
1512}
1513
1515 const SelectFileControl selectFileControl)
1516{
1517 /* Create the command and add it to the list of commands */
1518 mCardCommandManager->addRegularCommand(
1519 std::make_shared<CmdCardSelectFile>(mCalypsoCard->getCardClass(), selectFileControl));
1520
1521 return *this;
1522}
1523
1524CardTransactionManager& CardTransactionManagerAdapter::prepareGetData(const GetDataTag tag)
1525{
1526 /* Create the command and add it to the list of commands */
1527 switch (tag) {
1528 case GetDataTag::FCI_FOR_CURRENT_DF:
1529 mCardCommandManager->addRegularCommand(
1530 std::make_shared<CmdCardGetDataFci>(mCalypsoCard->getCardClass()));
1531 break;
1532 case GetDataTag::FCP_FOR_CURRENT_FILE:
1533 mCardCommandManager->addRegularCommand(
1534 std::make_shared<CmdCardGetDataFcp>(mCalypsoCard->getCardClass()));
1535 break;
1536 case GetDataTag::EF_LIST:
1537 mCardCommandManager->addRegularCommand(
1538 std::make_shared<CmdCardGetDataEfList>(mCalypsoCard->getCardClass()));
1539 break;
1540 case GetDataTag::TRACEABILITY_INFORMATION:
1541 mCardCommandManager->addRegularCommand(
1542 std::make_shared<CmdCardGetDataTraceabilityInformation>(mCalypsoCard->getCardClass()));
1543 break;
1544 default:
1545 std::stringstream ss;
1546 ss << tag;
1547 throw UnsupportedOperationException("Unsupported Get Data tag: " + ss.str());
1548 }
1549
1550 return *this;
1551}
1552
1554 const uint8_t sfi, const uint8_t recordNumber)
1555{
1556 return prepareReadRecord(sfi, recordNumber);
1557}
1558
1560 const uint8_t sfi,
1561 const uint8_t firstRecordNumber,
1562 const uint8_t numberOfRecords,
1563 const uint8_t recordSize)
1564{
1565 return prepareReadRecords(sfi,
1566 firstRecordNumber,
1567 firstRecordNumber + numberOfRecords - 1,
1568 recordSize);
1569}
1570
1572 const uint8_t sfi, const uint8_t countersNumber)
1573{
1574 return prepareReadCounter(sfi, countersNumber);
1575}
1576
1578 const uint8_t sfi, const uint8_t recordNumber)
1579{
1580 Assert::getInstance().isInRange(sfi,
1583 "sfi")
1584 .isInRange(recordNumber,
1587 RECORD_NUMBER);
1588
1589 if (mSessionState == SessionState::SESSION_OPEN &&
1590 !std::dynamic_pointer_cast<CardReader>(mCardReader)->isContactless()) {
1591 throw IllegalStateException("Explicit record size is expected inside a secure session in " \
1592 "contact mode.");
1593 }
1594
1595 auto cmdCardReadRecords =
1596 std::make_shared<CmdCardReadRecords>(mCalypsoCard->getCardClass(),
1597 sfi,
1598 recordNumber,
1600 static_cast<uint8_t>(0));
1601 mCardCommandManager->addRegularCommand(cmdCardReadRecords);
1602
1603 return *this;
1604}
1605
1607 const uint8_t sfi,
1608 const uint8_t fromRecordNumber,
1609 const uint8_t toRecordNumber,
1610 const uint8_t recordSize)
1611{
1612 Assert::getInstance().isInRange(sfi,
1615 "sfi")
1616 .isInRange(fromRecordNumber,
1619 "fromRecordNumber")
1620 .isInRange(toRecordNumber,
1621 fromRecordNumber,
1623 "toRecordNumber");
1624
1625 if (toRecordNumber == fromRecordNumber) {
1626 /* Create the command and add it to the list of commands */
1627 mCardCommandManager->addRegularCommand(
1628 std::make_shared<CmdCardReadRecords>(mCalypsoCard->getCardClass(),
1629 sfi,
1630 fromRecordNumber,
1632 recordSize));
1633 } else {
1634 /*
1635 * Manages the reading of multiple records taking into account the transmission capacity
1636 * of the card and the response format (2 extra bytes).
1637 * Multiple APDUs can be generated depending on record size and transmission capacity.
1638 */
1639 const CalypsoCardClass cardClass = mCalypsoCard->getCardClass();
1640 const uint8_t nbBytesPerRecord = recordSize + 2;
1641 const uint8_t nbRecordsPerApdu =
1642 static_cast<uint8_t>(mCalypsoCard->getPayloadCapacity() / nbBytesPerRecord);
1643 const uint8_t dataSizeMaxPerApdu = nbRecordsPerApdu * nbBytesPerRecord;
1644
1645 uint8_t currentRecordNumber = fromRecordNumber;
1646 uint8_t nbRecordsRemainingToRead = toRecordNumber - fromRecordNumber + 1;
1647 uint8_t currentLength;
1648
1649 while (currentRecordNumber < toRecordNumber) {
1650 currentLength = nbRecordsRemainingToRead <= nbRecordsPerApdu ?
1651 nbRecordsRemainingToRead * nbBytesPerRecord :
1652 dataSizeMaxPerApdu;
1653
1654 mCardCommandManager->addRegularCommand(
1655 std::make_shared<CmdCardReadRecords>(cardClass,
1656 sfi,
1657 currentRecordNumber,
1659 currentLength));
1660
1661 currentRecordNumber += (currentLength / nbBytesPerRecord);
1662 nbRecordsRemainingToRead -= (currentLength / nbBytesPerRecord);
1663 }
1664
1665 /* Optimization: prepare a read "one record" if possible for last iteration.*/
1666 if (currentRecordNumber == toRecordNumber) {
1667 mCardCommandManager->addRegularCommand(
1668 std::make_shared<CmdCardReadRecords>(cardClass,
1669 sfi,
1670 currentRecordNumber,
1672 recordSize));
1673 }
1674 }
1675
1676 return *this;
1677}
1678
1680 const uint8_t sfi,
1681 const uint8_t fromRecordNumber,
1682 const uint8_t toRecordNumber,
1683 const uint8_t offset,
1684 const uint8_t nbBytesToRead)
1685{
1686 if (mCalypsoCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3 &&
1687 mCalypsoCard->getProductType() != CalypsoCard::ProductType::LIGHT) {
1688 throw UnsupportedOperationException("The 'Read Record Multiple' command is not available "\
1689 "for this card.");
1690 }
1691
1692 Assert::getInstance().isInRange(sfi,
1695 "sfi")
1696 .isInRange(fromRecordNumber,
1699 "fromRecordNumber")
1700 .isInRange(toRecordNumber,
1701 fromRecordNumber,
1703 "toRecordNumber")
1704 .isInRange(offset,
1707 OFFSET)
1708 .isInRange(nbBytesToRead,
1711 "nbBytesToRead");
1712
1713 const CalypsoCardClass cardClass = mCalypsoCard->getCardClass();
1714 const uint8_t nbRecordsPerApdu =
1715 static_cast<uint8_t>(mCalypsoCard->getPayloadCapacity() / nbBytesToRead);
1716
1717 uint8_t currentRecordNumber = fromRecordNumber;
1718
1719 while (currentRecordNumber <= toRecordNumber) {
1720 mCardCommandManager->addRegularCommand(
1721 std::make_shared<CmdCardReadRecordMultiple>(cardClass,
1722 sfi,
1723 currentRecordNumber,
1724 offset,
1725 nbBytesToRead));
1726 currentRecordNumber += nbRecordsPerApdu;
1727 }
1728
1729 return *this;
1730}
1731
1733 const uint8_t sfi, const uint8_t offset, const uint8_t nbBytesToRead)
1734{
1735 if (mCalypsoCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3) {
1736 throw UnsupportedOperationException("The 'Read Binary' command is not available for this " \
1737 "card.");
1738 }
1739
1740 Assert::getInstance().isInRange(sfi,
1743 "sfi")
1744 .isInRange(offset,
1747 OFFSET)
1748 .greaterOrEqual(nbBytesToRead, 1, "nbBytesToRead");
1749
1750 /* C++: no need to check offset > 255, forced by value type */
1751 if (sfi > 0) {
1752 /* Tips to select the file: add a "Read Binary" command (read one byte at offset 0). */
1753 mCardCommandManager->addRegularCommand(
1754 std::make_shared<CmdCardReadBinary>(mCalypsoCard->getCardClass(),
1755 sfi,
1756 static_cast<uint8_t>(0),
1757 static_cast<uint8_t>(1)));
1758 }
1759
1760 const uint8_t payloadCapacity = mCalypsoCard->getPayloadCapacity();
1761 const CalypsoCardClass cardClass = mCalypsoCard->getCardClass();
1762
1763 uint8_t currentLength;
1764 uint8_t currentOffset = offset;
1765 uint8_t nbBytesRemainingToRead = nbBytesToRead;
1766
1767 do {
1768 currentLength = std::min(nbBytesRemainingToRead, payloadCapacity);
1769 mCardCommandManager->addRegularCommand(
1770 std::make_shared<CmdCardReadBinary>(cardClass, sfi, currentOffset, currentLength));
1771
1772 currentOffset += currentLength;
1773 nbBytesRemainingToRead -= currentLength;
1774 } while (nbBytesRemainingToRead > 0);
1775
1776 return *this;
1777}
1778
1780 const uint8_t sfi, const uint8_t nbCountersToRead)
1781{
1782 return prepareReadRecords(sfi, 1, 1, nbCountersToRead * 3);
1783}
1784
1786 const std::shared_ptr<SearchCommandData> data)
1787{
1788 if (mCalypsoCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3) {
1789 throw UnsupportedOperationException("The 'Search Record Multiple' command is not " \
1790 "available for this card.");
1791 }
1792
1793 auto dataAdapter = std::dynamic_pointer_cast<SearchCommandDataAdapter>(data);
1794 if (!dataAdapter) {
1795 throw IllegalArgumentException("The provided data must be an instance of " \
1796 "'SearchCommandDataAdapter' class.");
1797 }
1798
1799 Assert::getInstance().notNull(data, "data")
1800 .isInRange(dataAdapter->getSfi(),
1803 "sfi")
1804 .isInRange(dataAdapter->getRecordNumber(),
1807 "startAtRecord")
1808 .isInRange(dataAdapter->getOffset(),
1811 OFFSET)
1812 .isInRange(dataAdapter->getSearchData().size(),
1814 CalypsoCardConstant::DATA_LENGTH_MAX - dataAdapter->getOffset(),
1815 "searchData");
1816 if (!dataAdapter->getMask().empty()) {
1817 Assert::getInstance().isInRange(dataAdapter->getMask().size(),
1819 dataAdapter->getSearchData().size(),
1820 "mask");
1821 }
1822
1823 mCardCommandManager->addRegularCommand(
1824 std::make_shared<CmdCardSearchRecordMultiple>(mCalypsoCard->getCardClass(), dataAdapter));
1825
1826 return *this;
1827}
1828
1830 const uint8_t sfi, const std::vector<uint8_t>& recordData)
1831{
1832 Assert::getInstance().isInRange(sfi,
1835 "sfi");
1836
1837 /* Create the command and add it to the list of commands */
1838 mCardCommandManager->addRegularCommand(
1839 std::make_shared<CmdCardAppendRecord>(mCalypsoCard->getCardClass(), sfi, recordData));
1840
1841 return *this;
1842}
1843
1845 const uint8_t sfi,
1846 const uint8_t recordNumber,
1847 const std::vector<uint8_t>& recordData)
1848{
1849 Assert::getInstance().isInRange(sfi,
1852 "sfi")
1853 .isInRange(recordNumber,
1856 RECORD_NUMBER);
1857
1858 /* Create the command and add it to the list of commands */
1859 mCardCommandManager->addRegularCommand(
1860 std::make_shared<CmdCardUpdateRecord>(mCalypsoCard->getCardClass(),
1861 sfi,
1862 recordNumber,
1863 recordData));
1864
1865 return *this;
1866}
1867
1869 const uint8_t sfi,
1870 const uint8_t recordNumber,
1871 const std::vector<uint8_t>& recordData)
1872{
1873 Assert::getInstance().isInRange(sfi,
1876 "sfi")
1877 .isInRange(recordNumber,
1880 RECORD_NUMBER);
1881
1882 /* Create the command and add it to the list of commands */
1883 mCardCommandManager->addRegularCommand(
1884 std::make_shared<CmdCardWriteRecord>(mCalypsoCard->getCardClass(),
1885 sfi,
1886 recordNumber,
1887 recordData));
1888
1889 return *this;
1890}
1891
1893 const uint8_t sfi,
1894 const uint8_t offset,
1895 const std::vector<uint8_t>& data)
1896{
1897 return prepareUpdateOrWriteBinary(true, sfi, offset, data);
1898}
1899
1901 const uint8_t sfi,
1902 const uint8_t offset,
1903 const std::vector<uint8_t>& data)
1904{
1905 return prepareUpdateOrWriteBinary(false, sfi, offset, data);
1906}
1907
1908CardTransactionManager& CardTransactionManagerAdapter::prepareUpdateOrWriteBinary(
1909 const bool isUpdateCommand,
1910 const uint8_t sfi,
1911 const uint8_t offset,
1912 const std::vector<uint8_t>& data)
1913{
1914 if (mCalypsoCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3) {
1915 throw UnsupportedOperationException("The 'Update/Write Binary' command is not available " \
1916 "for this card.");
1917 }
1918
1919 Assert::getInstance().isInRange(sfi,
1922 "sfi")
1923 .isInRange(offset,
1926 OFFSET)
1927 .notEmpty(data, "data");
1928
1929 /* C++: no need to check offset > 255, forced by value type */
1930 if (sfi > 0) {
1931 /* Tips to select the file: add a "Read Binary" command (read one byte at offset 0) */
1932 mCardCommandManager->addRegularCommand(
1933 std::make_shared<CmdCardReadBinary>(mCalypsoCard->getCardClass(),
1934 sfi,
1935 static_cast<uint8_t>(0),
1936 static_cast<uint8_t>(1)));
1937 }
1938
1939 const uint8_t dataLength = static_cast<uint8_t>(data.size());
1940 const uint8_t payloadCapacity = mCalypsoCard->getPayloadCapacity();
1941 const CalypsoCardClass cardClass = mCalypsoCard->getCardClass();
1942
1943 uint8_t currentLength;
1944 uint8_t currentOffset = offset;
1945 uint8_t currentIndex = 0;
1946
1947 do {
1948 currentLength = static_cast<uint8_t>(
1949 std::min(static_cast<int>(dataLength - currentIndex),
1950 static_cast<int>(payloadCapacity)));
1951
1952 mCardCommandManager->addRegularCommand(
1953 std::make_shared<CmdCardUpdateOrWriteBinary>(
1954 isUpdateCommand,
1955 cardClass,
1956 sfi,
1957 currentOffset,
1958 Arrays::copyOfRange(data, currentIndex, currentIndex + currentLength)));
1959
1960 currentOffset += currentLength;
1961 currentIndex += currentLength;
1962 } while (currentIndex < dataLength);
1963
1964 return *this;
1965}
1966
1967CardTransactionManager& CardTransactionManagerAdapter::prepareIncreaseOrDecreaseCounter(
1968 const bool isDecreaseCommand,
1969 const uint8_t sfi,
1970 const uint8_t counterNumber,
1971 const int incDecValue)
1972{
1973 Assert::getInstance().isInRange(sfi,
1976 "sfi")
1977 .isInRange(counterNumber,
1980 "counterNumber")
1981 .isInRange(incDecValue,
1984 "incDecValue");
1985
1986 /* Create the command and add it to the list of commands */
1987 mCardCommandManager->addRegularCommand(
1988 std::make_shared<CmdCardIncreaseOrDecrease>(isDecreaseCommand,
1989 mCalypsoCard->getCardClass(),
1990 sfi,
1991 counterNumber,
1992 incDecValue));
1993
1994 return *this;
1995}
1996
1998 const uint8_t sfi, const uint8_t counterNumber, const int incValue)
1999{
2000 return prepareIncreaseOrDecreaseCounter(false, sfi, counterNumber, incValue);
2001}
2002
2004 const uint8_t sfi, const uint8_t counterNumber, const int decValue)
2005{
2006 return prepareIncreaseOrDecreaseCounter(true, sfi, counterNumber, decValue);
2007}
2008
2010 const uint8_t sfi,
2011 const std::map<const int, const int>& counterNumberToIncValueMap)
2012{
2013 return prepareIncreaseOrDecreaseCounters(false, sfi, counterNumberToIncValueMap);
2014}
2015
2017 const uint8_t sfi,
2018 const std::map<const int, const int>& counterNumberToDecValueMap)
2019{
2020 return prepareIncreaseOrDecreaseCounters(true, sfi, counterNumberToDecValueMap);
2021}
2022
2024{
2025 if (!mCalypsoCard->isPinFeatureAvailable()) {
2026 throw UnsupportedOperationException(PIN_NOT_AVAILABLE_ERROR);
2027 }
2028
2029 /* Create the command and add it to the list of commands */
2030 mCardCommandManager->addRegularCommand(
2031 std::make_shared<CmdCardVerifyPin>(mCalypsoCard->getCardClass()));
2032
2033 return *this;
2034}
2035
2036CardTransactionManager& CardTransactionManagerAdapter::prepareSvGet(const SvOperation svOperation,
2037 const SvAction svAction)
2038{
2039 if (!mCalypsoCard->isSvFeatureAvailable()) {
2040 throw UnsupportedOperationException("Stored Value is not available for this card.");
2041 }
2042
2043 if (std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
2044 ->isSvLoadAndDebitLogEnabled() &&
2045 !mCalypsoCard->isExtendedModeSupported()) {
2046 /*
2047 * @see Calypso Layer ID 8.09/8.10 (200108): both reload and debit logs are requested
2048 * for a non rev3.2 card add two SvGet commands (for RELOAD then for DEBIT).
2049 * CL-SV-GETNUMBER.1
2050 */
2051 const SvOperation operation1 = SvOperation::RELOAD == svOperation ? SvOperation::DEBIT :
2052 SvOperation::RELOAD;
2053 mCardCommandManager->addStoredValueCommand(
2054 std::make_shared<CmdCardSvGet>(mCalypsoCard->getCardClass(), mCalypsoCard, operation1),
2055 operation1);
2056 }
2057
2058 mCardCommandManager->addStoredValueCommand(
2059 std::make_shared<CmdCardSvGet>(mCalypsoCard->getCardClass(), mCalypsoCard, svOperation),
2060 svOperation);
2061
2062 mSvAction = svAction;
2063
2064 return *this;
2065}
2066
2068 const int amount,
2069 const std::vector<uint8_t>& date,
2070 const std::vector<uint8_t>& time,
2071 const std::vector<uint8_t>& free)
2072{
2073 /* Create the initial command with the application data */
2074 auto svReloadCmdBuild = std::make_shared<CmdCardSvReload>(mCalypsoCard,
2075 amount,
2076 mCalypsoCard->getSvKvc(),
2077 date,
2078 time,
2079 free);
2080
2081 /* Get the security data from the SAM */
2082 std::vector<uint8_t> svReloadComplementaryData;
2083 try {
2084 svReloadComplementaryData =
2085 mSamCommandProcessor->getSvReloadComplementaryData(svReloadCmdBuild,
2086 mCalypsoCard->getSvGetHeader(),
2087 mCalypsoCard->getSvGetData());
2088 } catch (const CalypsoSamCommandException& e) {
2089 throw SamAnomalyException(SAM_COMMAND_ERROR +
2090 "preparing the SV reload command: " +
2091 e.getCommand().getName(),
2092 std::make_shared<CalypsoSamCommandException>(e));
2093 } catch (const ReaderBrokenCommunicationException& e) {
2094 throw SamIOException(SAM_READER_COMMUNICATION_ERROR + "preparing the SV reload command.",
2095 std::make_shared<ReaderBrokenCommunicationException>(e));
2096 } catch (const CardBrokenCommunicationException& e) {
2097 throw SamIOException(SAM_COMMUNICATION_ERROR + "preparing the SV reload command.",
2098 std::make_shared<CardBrokenCommunicationException>(e));
2099 }
2100
2101 /* Finalize the SvReload command with the data provided by the SAM */
2102 svReloadCmdBuild->finalizeCommand(svReloadComplementaryData);
2103
2104 /* Create and keep the CalypsoCardCommand */
2105 mCardCommandManager->addStoredValueCommand(svReloadCmdBuild, SvOperation::RELOAD);
2106
2107 return *this;
2108}
2109
2110CardTransactionManager& CardTransactionManagerAdapter::prepareSvReload(const int amount)
2111{
2112 const std::vector<uint8_t> zero = {0x00, 0x00};
2113
2114 prepareSvReload(amount, zero, zero, zero);
2115
2116 return *this;
2117}
2118
2120 const int amount,
2121 const std::vector<uint8_t>& date,
2122 const std::vector<uint8_t>& time)
2123{
2124 try {
2125 if (SvAction::DO == mSvAction) {
2126 prepareInternalSvDebit(amount, date, time);
2127 } else {
2128 prepareInternalSvUndebit(amount, date, time);
2129 }
2130 } catch (const CalypsoSamCommandException& e) {
2131 throw SamAnomalyException(SAM_COMMAND_ERROR +
2132 "preparing the SV debit/undebit command: " +
2133 e.getCommand().getName(),
2134 std::make_shared<CalypsoSamCommandException>(e));
2135 } catch (const ReaderBrokenCommunicationException& e) {
2136 throw SamIOException(SAM_READER_COMMUNICATION_ERROR +
2137 "preparing the SV debit/undebit command.",
2138 std::make_shared<ReaderBrokenCommunicationException>(e));
2139 } catch (const CardBrokenCommunicationException& e) {
2140 throw SamIOException(SAM_COMMUNICATION_ERROR +
2141 "preparing the SV debit/undebit command.",
2142 std::make_shared<CardBrokenCommunicationException>(e));
2143 }
2144
2145 return *this;
2146}
2147
2148void CardTransactionManagerAdapter::prepareInternalSvDebit(const int amount,
2149 const std::vector<uint8_t>& date,
2150 const std::vector<uint8_t>& time)
2151{
2152 if (!std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
2153 ->isSvNegativeBalanceAuthorized() &&
2154 (mCalypsoCard->getSvBalance() - amount) < 0) {
2155 throw IllegalStateException("Negative balances not allowed.");
2156 }
2157
2158 /* Create the initial command with the application data */
2159 auto svDebitCmdBuild = std::make_shared<CmdCardSvDebit>(mCalypsoCard,
2160 amount,
2161 mCalypsoCard->getSvKvc(),
2162 date,
2163 time);
2164
2165 /* Get the security data from the SAM */
2166 const std::vector<uint8_t> svDebitComplementaryData =
2167 mSamCommandProcessor->getSvDebitComplementaryData(svDebitCmdBuild,
2168 mCalypsoCard->getSvGetHeader(),
2169 mCalypsoCard->getSvGetData());
2170
2171 /* Finalize the SvDebit command with the data provided by the SAM */
2172 svDebitCmdBuild->finalizeCommand(svDebitComplementaryData);
2173
2174 /* Create and keep the CalypsoCardCommand */
2175 mCardCommandManager->addStoredValueCommand(svDebitCmdBuild, SvOperation::DEBIT);
2176}
2177
2178void CardTransactionManagerAdapter::prepareInternalSvUndebit(const int amount,
2179 const std::vector<uint8_t>& date,
2180 const std::vector<uint8_t>& time)
2181{
2182 /* Create the initial command with the application data */
2183 auto svUndebitCmdBuild = std::make_shared<CmdCardSvUndebit>(mCalypsoCard,
2184 amount,
2185 mCalypsoCard->getSvKvc(),
2186 date,
2187 time);
2188
2189 /* Get the security data from the SAM */
2190 std::vector<uint8_t> svDebitComplementaryData;
2191 svDebitComplementaryData =
2192 mSamCommandProcessor->getSvUndebitComplementaryData(svUndebitCmdBuild,
2193 mCalypsoCard->getSvGetHeader(),
2194 mCalypsoCard->getSvGetData());
2195
2196 /* Finalize the SvUndebit command with the data provided by the SAM */
2197 svUndebitCmdBuild->finalizeCommand(svDebitComplementaryData);
2198
2199 /* Create and keep the CalypsoCardCommand */
2200 mCardCommandManager->addStoredValueCommand(svUndebitCmdBuild, SvOperation::DEBIT);
2201}
2202
2203CardTransactionManager& CardTransactionManagerAdapter::prepareSvDebit(const int amount)
2204{
2205 const std::vector<uint8_t> zero = {0x00, 0x00};
2206
2207 prepareSvDebit(amount, zero, zero);
2208
2209 return *this;
2210}
2211
2213{
2214 if (!mCalypsoCard->isSvFeatureAvailable()) {
2215 throw UnsupportedOperationException("Stored Value is not available for this card.");
2216 }
2217
2218 if (mCalypsoCard->getApplicationSubtype() !=
2220 throw UnsupportedOperationException("The currently selected application is not an SV " \
2221 "application.");
2222 }
2223
2224 /* Reset SV data in CalypsoCard if any */
2225 const std::vector<uint8_t> dummy;
2226 mCalypsoCard->setSvData(0, dummy, dummy, 0, 0, nullptr, nullptr);
2228 1,
2232 1,
2235
2236 return *this;
2237}
2238
2240{
2241 if (mCalypsoCard->isDfInvalidated()) {
2242 throw IllegalStateException("This card is already invalidated.");
2243 }
2244
2245 mCardCommandManager->addRegularCommand(
2246 std::make_shared<CmdCardInvalidate>(mCalypsoCard->getCardClass()));
2247
2248 return *this;
2249}
2250
2252{
2253 if (!mCalypsoCard->isDfInvalidated()) {
2254 throw IllegalStateException("This card is not invalidated.");
2255 }
2256
2257 mCardCommandManager->addRegularCommand(
2258 std::make_shared<CmdCardRehabilitate>(mCalypsoCard->getCardClass()));
2259
2260 return *this;
2261}
2262
2263/* APDU RESPONSE ADAPTER ------------------------------------------------------------------------ */
2264
2265CardTransactionManagerAdapter::ApduResponseAdapter::ApduResponseAdapter(
2266 const std::vector<uint8_t>& apdu)
2267: mApdu(apdu),
2268 mStatusWord(((apdu[apdu.size() - 2] & 0x000000FF) << 8) + (apdu[apdu.size() - 1] & 0x000000FF)) {}
2269
2270const std::vector<uint8_t>& CardTransactionManagerAdapter::ApduResponseAdapter::getApdu() const
2271{
2272 return mApdu;
2273}
2274
2275const std::vector<uint8_t> CardTransactionManagerAdapter::ApduResponseAdapter::getDataOut() const
2276{
2277 return Arrays::copyOfRange(mApdu, 0, mApdu.size() - 2);
2278}
2279
2280int CardTransactionManagerAdapter::ApduResponseAdapter::getStatusWord() const
2281{
2282 return mStatusWord;
2283}
2284
2285std::ostream& operator<<(std::ostream& os, const CardTransactionManagerAdapter::ApduResponseAdapter& ara)
2286{
2287 os << "APDU_RESPONSE_ADAPTER: {"
2288 << "APDU: " << ara.getApdu() << ", "
2289 << "STATUS_WORD: " << ara.getStatusWord()
2290 << "}";
2291
2292 return os;
2293}
2294
2295
2296std::ostream& operator<<(std::ostream& os, const std::shared_ptr<CardTransactionManagerAdapter::ApduResponseAdapter> ara)
2297{
2298 if (ara == nullptr) {
2299 os << "APDU_RESPONSE_ADAPTER: null";
2300 } else {
2301 os << *ara;
2302 }
2303
2304 return os;
2305}
2306
2307/* SESSION STATE -------------------------------------------------------------------------------- */
2308
2309std::ostream& operator<<(std::ostream& os, const CardTransactionManagerAdapter::SessionState ss)
2310{
2311 switch (ss) {
2313 os << "SESSION_UNINITIALIZED";
2314 break;
2316 os << "SESSION_OPEN";
2317 break;
2319 os << "SESSION_CLOSED";
2320 break;
2321 default:
2322 os << "UNKONWN";
2323 break;
2324 }
2325
2326 return os;
2327}
2328
2329}
2330}
2331}
static const CalypsoCardCommand DECREASE
static const CalypsoCardCommand SV_RELOAD
static const CalypsoCardCommand INCREASE
static const CalypsoCardCommand SV_DEBIT
static const CalypsoCardCommand READ_RECORDS
static const CalypsoCardCommand DECREASE_MULTIPLE
static const CalypsoCardCommand SV_UNDEBIT
static const CalypsoCardCommand INCREASE_MULTIPLE
static void updateCalypsoCard(std::shared_ptr< CalypsoCardAdapter > calypsoCard, const std::shared_ptr< AbstractCardCommand > command, const std::shared_ptr< ApduResponseApi > apduResponse, const bool isSessionOpen)
virtual const std::string & getName() const =0
const std::shared_ptr< CalypsoCard > getCalypsoCard() const override
CardTransactionManager & processChangePin(const std::vector< uint8_t > &newPin) override
CardTransactionManager & prepareUpdateBinary(const uint8_t sfi, const uint8_t offset, const std::vector< uint8_t > &data) final
const std::shared_ptr< CardSecuritySetting > getCardSecuritySetting() const override
CardTransactionManager & prepareSetCounter(const uint8_t sfi, const uint8_t counterNumber, const int newValue) final
CardTransactionManager & prepareReadRecordFile(const uint8_t sfi, const uint8_t recordNumber) final
CardTransactionManager & prepareReadRecord(const uint8_t sfi, const uint8_t recordNumber) override
CardTransactionManager & prepareIncreaseCounters(const uint8_t sfi, const std::map< const int, const int > &counterNumberToIncValueMap) final
CardTransactionManager & processChangeKey(const uint8_t keyIndex, const uint8_t newKif, const uint8_t newKvc, const uint8_t issuerKif, const uint8_t issuerKvc) override
CardTransactionManager & prepareDecreaseCounter(const uint8_t sfi, const uint8_t counterNumber, const int decValue) final
CardTransactionManager & prepareReadRecords(const uint8_t sfi, const uint8_t fromRecordNumber, const uint8_t toRecordNumber, const uint8_t recordSize) override
CardTransactionManager & prepareSelectFile(const std::vector< uint8_t > &lid) final
CardTransactionManager & prepareReadBinary(const uint8_t sfi, const uint8_t offset, const uint8_t nbBytesToRead) override
const std::shared_ptr< CardReader > getCardReader() const override
CardTransactionManager & prepareWriteRecord(const uint8_t sfi, const uint8_t recordNumber, const std::vector< uint8_t > &recordData) final
CardTransactionManagerAdapter(const std::shared_ptr< CardReader > cardReader, const std::shared_ptr< CalypsoCard > calypsoCard, const std::shared_ptr< CardSecuritySetting > cardSecuritySetting)
CardTransactionManager & prepareGetData(const GetDataTag tag) override
CardTransactionManager & processOpening(const WriteAccessLevel writeAccessLevel) final
CardTransactionManager & processVerifyPin(const std::vector< uint8_t > &pin) final
CardTransactionManager & prepareReadCounterFile(const uint8_t sfi, const uint8_t countersNumber) final
CardTransactionManager & prepareUpdateRecord(const uint8_t sfi, const uint8_t recordNumber, const std::vector< uint8_t > &recordData) final
CardTransactionManager & prepareIncreaseCounter(const uint8_t sfi, const uint8_t counterNumber, const int incValue) final
CardTransactionManager & prepareSvDebit(const int amount, const std::vector< uint8_t > &date, const std::vector< uint8_t > &time) final
CardTransactionManager & prepareReadCounter(const uint8_t sfi, const uint8_t nbCountersToRead) override
CardTransactionManager & prepareAppendRecord(const uint8_t sfi, const std::vector< uint8_t > &recordData) final
CardTransactionManager & prepareReadRecordsPartially(const uint8_t sfi, const uint8_t fromRecordNumber, const uint8_t toRecordNumber, const uint8_t offset, const uint8_t nbBytesToRead) override
CardTransactionManager & prepareSvGet(const SvOperation svOperation, const SvAction svAction) final
CardTransactionManager & prepareWriteBinary(const uint8_t sfi, const uint8_t offset, const std::vector< uint8_t > &data) final
CardTransactionManager & prepareSvReload(const int amount, const std::vector< uint8_t > &date, const std::vector< uint8_t > &time, const std::vector< uint8_t > &free) final
CardTransactionManager & prepareSearchRecords(const std::shared_ptr< SearchCommandData > data) override
CardTransactionManager & prepareDecreaseCounters(const uint8_t sfi, const std::map< const int, const int > &counterNumberToDecValueMap) final
static const std::shared_ptr< ApduRequestAdapter > getApduRequest(const CalypsoCardClass calypsoCardClass)
std::ostream & operator<<(std::ostream &os, const std::shared_ptr< ApduRequestAdapter > ara)