Keyple Card Calypso C++ Library 2.2.5.6
Reference Terminal Reader API for C++
CardTransactionManagerAdapter.cpp
Go to the documentation of this file.
1/**************************************************************************************************
2 * Copyright (c) 2023 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 "CardIOException.h"
20#include "CardSignatureNotVerifiableException.h"
21#include "InconsistentDataException.h"
22#include "ReaderIOException.h"
23#include "SamIOException.h"
24#include "SessionBufferOverflowException.h"
25#include "UnauthorizedKeyException.h"
26#include "UnexpectedCommandStatusException.h"
27#include "UnexpectedStatusWordException.h"
28
29/* Calypsonet Terminal Card */
30#include "ApduResponseApi.h"
31#include "ApduRequestSpi.h"
32#include "CardBrokenCommunicationException.h"
33#include "CardResponseApi.h"
34#include "ReaderBrokenCommunicationException.h"
35#include "SelectFileException.h"
36
37/* Keyple Card Calypso */
38#include "CalypsoCardConstant.h"
43#include "CardRequestAdapter.h"
46#include "CmdCardAppendRecord.h"
47#include "CmdCardChangeKey.h"
48#include "CmdCardChangePin.h"
49#include "CmdCardCloseSession.h"
50#include "CmdCardGetChallenge.h"
52#include "CmdCardGetDataFci.h"
53#include "CmdCardGetDataFcp.h"
57#include "CmdCardInvalidate.h"
58#include "CmdCardOpenSession.h"
59#include "CmdCardReadBinary.h"
61#include "CmdCardReadRecords.h"
62#include "CmdCardRehabilitate.h"
64#include "CmdCardSelectFile.h"
65#include "CmdCardSvGet.h"
67#include "CmdCardUpdateRecord.h"
68#include "CmdCardVerifyPin.h"
69#include "CmdCardWriteRecord.h"
71
72/* Keyple Core Util */
73#include "Arrays.h"
74#include "ByteArrayUtil.h"
76#include "HexUtil.h"
77#include "IllegalStateException.h"
78#include "KeypleAssert.h"
79#include "KeypleStd.h"
80#include "MapUtils.h"
81#include "System.h"
82#include "UnsupportedOperationException.h"
83
84namespace keyple {
85namespace card {
86namespace calypso {
87
88using namespace calypsonet::terminal::calypso::transaction;
89using namespace calypsonet::terminal::card;
90using namespace calypsonet::terminal::card::spi;
91using namespace keyple::core::util;
92using namespace keyple::core::util::cpp;
93using namespace keyple::core::util::cpp::exception;
94
95/* CARD TRANSACTION MANAGER ADAPTER ------------------------------------------------------------- */
96const std::string CardTransactionManagerAdapter::PATTERN_1_BYTE_HEX = "%020Xh";
97
98const std::string CardTransactionManagerAdapter::MSG_CARD_READER_COMMUNICATION_ERROR =
99 "A communication error with the card reader occurred ";
100const std::string CardTransactionManagerAdapter::MSG_CARD_COMMUNICATION_ERROR =
101 "A communication error with the card occurred ";
102const std::string CardTransactionManagerAdapter::MSG_CARD_COMMAND_ERROR =
103 "A card command error occurred ";
104
105const std::string CardTransactionManagerAdapter::MSG_PIN_NOT_AVAILABLE =
106 "PIN is not available for this card.";
107const std::string CardTransactionManagerAdapter::MSG_CARD_SIGNATURE_NOT_VERIFIABLE =
108 "Unable to verify the card signature associated to the successfully closed secure session.";
109const std::string CardTransactionManagerAdapter::MSG_CARD_SIGNATURE_NOT_VERIFIABLE_SV =
110 "Unable to verify the card signature associated to the SV operation.";
111
112const std::string CardTransactionManagerAdapter::RECORD_NUMBER = "record number";
113const std::string CardTransactionManagerAdapter::OFFSET = "offset";
114
115const int CardTransactionManagerAdapter::SESSION_BUFFER_CMD_ADDITIONAL_COST = 6;
116const int CardTransactionManagerAdapter::APDU_HEADER_LENGTH = 5;
117
118
119const std::shared_ptr<ApduResponseApi> CardTransactionManagerAdapter::RESPONSE_OK =
120 std::make_shared<ApduResponseAdapter>(std::vector<uint8_t>({0x90, 0x00}));
121const std::shared_ptr<ApduResponseApi> CardTransactionManagerAdapter::RESPONSE_OK_POSTPONED =
122 std::make_shared<ApduResponseAdapter>(std::vector<uint8_t>({0x62, 0x00}));
123
125 const std::shared_ptr<ProxyReaderApi> cardReader,
126 const std::shared_ptr<CalypsoCardAdapter> card,
127 const std::shared_ptr<CardSecuritySettingAdapter> securitySetting)
129 card,
130 std::dynamic_pointer_cast<CommonSecuritySettingAdapter<CardSecuritySettingAdapter>>(securitySetting),
131 std::vector<std::vector<uint8_t>>()),
132 mCardReader(cardReader),
133 mCard(card),
134 mSecuritySetting(securitySetting),
135 mModificationsCounter(card->getModificationsCounter())
136{
137 if (securitySetting != nullptr && securitySetting->getControlSam() != nullptr) {
138 /* Secure operations mode */
139 mControlSamTransactionManager =
140 std::make_shared<CardControlSamTransactionManagerAdapter>(card,
141 securitySetting,
143
144 } else {
145 /* Non-secure operations mode */
146 mControlSamTransactionManager = nullptr;
147 }
148}
149
150const std::shared_ptr<CardReader> CardTransactionManagerAdapter::getCardReader() const
151{
152 return std::dynamic_pointer_cast<CardReader>(mCardReader);
153}
154
155const std::shared_ptr<CalypsoCard> CardTransactionManagerAdapter::getCalypsoCard() const
156{
157 return mCard;
158}
159
160const std::shared_ptr<CardSecuritySetting> CardTransactionManagerAdapter::getCardSecuritySetting()
161 const
162{
163 return std::dynamic_pointer_cast<CardSecuritySetting>(getSecuritySetting());
164}
165
166void CardTransactionManagerAdapter::checkControlSam() const
167{
168 if (mControlSamTransactionManager == nullptr) {
169 throw IllegalStateException("Control SAM is not set.");
170 }
171}
172
173void CardTransactionManagerAdapter::processSamPreparedCommands()
174{
175 if (mControlSamTransactionManager != nullptr) {
176 mControlSamTransactionManager->processCommands();
177 }
178}
179
180void CardTransactionManagerAdapter::processAtomicOpening(
181 std::vector<std::shared_ptr<AbstractApduCommand>>& cardCommands)
182{
183 if (mSecuritySetting == nullptr) {
184 throw IllegalStateException("No security settings are available.");
185 }
186
187 mCard->backupFiles();
188 mNbPostponedData = 0;
189
190 /*
191 * Let's check if we have a read record command at the top of the command list.
192 * If so, then the command is withdrawn in favour of its equivalent executed at the same
193 * time as the open secure session command.
194 * The sfi and record number to be read when the open secure session command is executed.
195 * The default value is 0 (no record to read) but we will optimize the exchanges if a read
196 * record command has been prepared.
197 */
198 uint8_t sfi = 0;
199 uint8_t recordNumber = 0;
200 uint8_t recordSize = 0;
201
202 if (!cardCommands.empty()) {
203
204 const auto cardCommand = std::dynamic_pointer_cast<AbstractCardCommand>(cardCommands[0]);
205 if (cardCommand->getCommandRef() == CalypsoCardCommand::READ_RECORDS &&
206 std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getReadMode() ==
208 sfi = std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getSfi();
209 recordNumber =
210 std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getFirstRecordNumber();
211 recordSize =
212 std::dynamic_pointer_cast<CmdCardReadRecords>(cardCommand)->getRecordSize();
213 cardCommands.erase(cardCommands.begin());
214 }
215 }
216
217 /* Compute the SAM challenge and process all pending SAM commands */
218 const std::vector<uint8_t> samChallenge = processSamGetChallenge();
219
220 /* Build the "Open Secure Session" card command */
221 auto cmdCardOpenSession =
222 std::make_shared<CmdCardOpenSession>(
223 mCard,
224 static_cast<uint8_t>(static_cast<int>(mWriteAccessLevel) + 1),
225 samChallenge,
226 sfi,
227 recordNumber,
228 recordSize,
229 isExtendedModeAllowed());
230
231 /* Add the "Open Secure Session" card command in first position */
232 cardCommands.insert(cardCommands.begin(), cmdCardOpenSession);
233
234 /* List of APDU requests to hold Open Secure Session and other optional commands */
235 const std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests =
236 getApduRequests(cardCommands);
237
238 /* Wrap the list of c-APDUs into a card requets */
239 auto cardRequest = std::make_shared<CardRequestAdapter>(apduRequests, true);
240
241 mIsSessionOpen = true;
242
243 /* Open a secure session, transmit the commands to the card and keep channel open */
244 const std::shared_ptr<CardResponseApi> cardResponse =
245 transmitCardRequest(cardRequest, ChannelControl::KEEP_OPEN);
246
247 /* Retrieve the list of R-APDUs */
248 const std::vector<std::shared_ptr<ApduResponseApi>> apduResponses =
249 cardResponse->getApduResponses();
250
251 /* Parse all the responses and fill the CalypsoCard object with the command data */
252 try {
253
254 /*
255 * C++: cannot cast std::vector<std::shared_ptr<derived>> into
256 * std::vector<std::shared_ptr<base>> automatically. Need to copy into another vector.
257 */
258 std::vector<std::shared_ptr<AbstractCardCommand>> copy;
259 for (int i = 0; i < static_cast<int>(cardCommands.size()); i++) {
260
261 copy.push_back(std::dynamic_pointer_cast<AbstractCardCommand>(cardCommands[i]));
262 }
263
264 parseApduResponses(copy, apduResponses);
265
266 } catch (const CardCommandException& e) {
267
268 throw UnexpectedCommandStatusException(MSG_CARD_COMMAND_ERROR +
269 "while processing the response to open session: " +
270 e.getCommand().getName() +
272 std::make_shared<CardCommandException>(e));
273
274 } catch (const InconsistentDataException& e) {
275
276 throw InconsistentDataException(e.getMessage() + getTransactionAuditDataAsString());
277 }
278
279 /* Build the "Digest Init" SAM command from card Open Session */
280
281 /* The card KIF/KVC (KVC may be null for card Rev 1.0) */
282 const std::shared_ptr<uint8_t> cardKif = cmdCardOpenSession->getSelectedKif();
283 const std::shared_ptr<uint8_t> cardKvc = cmdCardOpenSession->getSelectedKvc();
284
285 const std::string logCardKif = cardKif != nullptr ? std::to_string(*cardKif) : "null";
286 const std::string logCardKvc = cardKvc != nullptr ? std::to_string(*cardKvc) : "null";
287
288 mLogger->debug("processAtomicOpening => opening: CARD_CHALLENGE=%, CARD_KIF=%, CARD_KVC=%\n",
289 HexUtil::toHex(cmdCardOpenSession->getCardChallenge()),
290 logCardKif,
291 logCardKvc);
292
293 const std::shared_ptr<uint8_t> kvc =
294 mControlSamTransactionManager->computeKvc(mWriteAccessLevel, cardKvc);
295 const std::shared_ptr<uint8_t> kif =
296 mControlSamTransactionManager->computeKif(mWriteAccessLevel, cardKif, kvc);
297
298 if (!mSecuritySetting->isSessionKeyAuthorized(kif, kvc)) {
299
300 const std::string logKif = kif != nullptr ? std::to_string(*kif) : "null";
301 const std::string logKvc = kvc != nullptr ? std::to_string(*kvc) : "null";
302
303 throw UnauthorizedKeyException("Unauthorized key error: " \
304 "KIF=" + logKif + ", " +
305 "KVC=" + logKvc + " " +
307 }
308
309 /* Initialize a new SAM session. */
310 mControlSamTransactionManager
311 ->initializeSession(apduResponses[0]->getDataOut(), *kif, *kvc, false, false);
312
313 /*
314 * Add all commands data to the digest computation. The first command in the list is the
315 * open secure session command. This command is not included in the digest computation, so
316 * we skip it and start the loop at index 1.
317 */
318 mControlSamTransactionManager->updateSession(apduRequests, apduResponses, 1);
319}
320
321void CardTransactionManagerAdapter::abortSecureSessionSilently()
322{
323 if (mIsSessionOpen) {
324
325 try {
326
328
329 } catch (const RuntimeException& e) {
330
331 mLogger->warn("An error occurred while aborting the current secure session: %",
332 e.getMessage());
333 }
334
335 mIsSessionOpen = false;
336 }
337}
338
340 const uint8_t sfi, const uint8_t counterNumber, const int newValue)
341{
342 std::shared_ptr<int> oldValue;
343
344 const std::shared_ptr<ElementaryFile> ef = mCard->getFileBySfi(sfi);
345 if (ef != nullptr) {
346
347 oldValue = ef->getData()->getContentAsCounterValue(counterNumber);
348 }
349
350 if (oldValue == nullptr) {
351
352 throw IllegalStateException("The value for counter " + std::to_string(counterNumber) +
353 " in file " + std::to_string(sfi) + " is not available");
354 }
355
356 const int delta = newValue - *oldValue;
357 if (delta > 0) {
358
359 mLogger->trace("Increment counter % (file %) from % to %\n",
360 counterNumber,
361 sfi,
362 newValue - delta,
363 newValue);
364
365 prepareIncreaseCounter(sfi, counterNumber, delta);
366
367 } else if (delta < 0) {
368
369 mLogger->trace("Decrement counter % (file %) from % to %\n",
370 counterNumber,
371 sfi,
372 newValue - delta,
373 newValue);
374
375 prepareDecreaseCounter(sfi, counterNumber, -delta);
376
377 } else {
378
379 mLogger->info("The counter % (SFI %) is already set to the desired value %\n",
380 counterNumber,
381 sfi,
382 newValue);
383 }
384
385 return *this;
386}
387
388CardTransactionManager& CardTransactionManagerAdapter::prepareIncreaseOrDecreaseCounters(
389 const bool isDecreaseCommand,
390 const uint8_t sfi,
391 const std::map<const int, const int>& counterNumberToIncDecValueMap)
392{
393 Assert::getInstance().isInRange((int) sfi,
396 "sfi")
397 .isInRange(counterNumberToIncDecValueMap.size(),
400 "counterNumberToIncDecValueMap");
401
402 for (const auto& entry : counterNumberToIncDecValueMap) {
403
404 Assert::getInstance().isInRange(entry.first,
407 "counterNumberToIncDecValueMapKey")
408 .isInRange(entry.second,
411 "counterNumberToIncDecValueMapValue");
412 }
413
414 if (mCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3 &&
415 mCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_2) {
416
417 for (const auto& entry : counterNumberToIncDecValueMap) {
418
419 if (isDecreaseCommand) {
420
421 prepareDecreaseCounter(sfi, entry.first, entry.second);
422
423 } else {
424
425 prepareIncreaseCounter(sfi, entry.first, entry.second);
426 }
427 }
428
429 } else {
430
431 const int nbCountersPerApdu = mCard->getPayloadCapacity() / 4;
432
433 if (static_cast<int>(counterNumberToIncDecValueMap.size()) <= nbCountersPerApdu) {
434
435 /* Create the command and add it to the list of commands */
436 mCardCommands.push_back(
437 std::make_shared<CmdCardIncreaseOrDecreaseMultiple>(
438 isDecreaseCommand,
439 mCard,
440 sfi,
441 counterNumberToIncDecValueMap));
442
443 } else {
444
445 /*
446 * The number of counters exceeds the payload capacity, let's split into several apdu
447 * commands
448 */
449 int i = 0;
450 std::map<const int, const int> map;
451
452 for (const auto& entry : counterNumberToIncDecValueMap) {
453
454 i++;
455
456 map.insert({entry.first, entry.second});
457
458 if (i == nbCountersPerApdu) {
459
460 mCardCommands.push_back(
461 std::make_shared<CmdCardIncreaseOrDecreaseMultiple>(
462 isDecreaseCommand,
463 mCard,
464 sfi,
465 map));
466
467 i = 0;
468 map.clear();
469 }
470 }
471
472 if (!map.empty()) {
473
474 mCardCommands.push_back(
475 std::make_shared<CmdCardIncreaseOrDecreaseMultiple>(
476 isDecreaseCommand,
477 mCard,
478 sfi,
479 map));
480 }
481 }
482 }
483
484 return *this;
485}
486
487void CardTransactionManagerAdapter::processAtomicCardCommands(
488 const std::vector<std::shared_ptr<AbstractApduCommand>> cardCommands,
489 const ChannelControl channelControl)
490{
491 /* Get the list of C-APDU to transmit */
492 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests = getApduRequests(cardCommands);
493
494 /* Wrap the list of C-APDUs into a card request */
495 std::shared_ptr<CardRequestSpi> cardRequest =
496 std::make_shared<CardRequestAdapter>(apduRequests, true);
497
498 /* Transmit the commands to the card */
499 const std::shared_ptr<CardResponseApi> cardResponse =
500 transmitCardRequest(cardRequest, channelControl);
501
502 /* Retrieve the list of R-APDUs */
503 const std::vector<std::shared_ptr<ApduResponseApi>> apduResponses =
504 cardResponse->getApduResponses();
505
506 /*
507 * If this method is invoked within a secure session, then add all commands data to the digest
508 * computation.
509 */
510 if (mIsSessionOpen) {
511
512 mControlSamTransactionManager->updateSession(apduRequests, apduResponses, 0);
513 }
514
515 try {
516
517 /*
518 * C++: cannot cast std::vector<std::shared_ptr<derived>> into
519 * std::vector<std::shared_ptr<base>> automatically. Need to copy into another vector.
520 */
521 std::vector<std::shared_ptr<AbstractCardCommand>> copy;
522
523 for (int i = 0; i < static_cast<int>(cardCommands.size()); i++) {
524
525 copy.push_back(std::dynamic_pointer_cast<AbstractCardCommand>(cardCommands[i]));
526 }
527
528 parseApduResponses(copy, apduResponses);
529
530 } catch (const CardCommandException& e) {
531
532 throw UnexpectedCommandStatusException(MSG_CARD_COMMAND_ERROR +
533 "while processing responses to card commands: " +
534 e.getCommand().getName() +
536 std::make_shared<CardCommandException>(e));
537
538 } catch (const InconsistentDataException& e) {
539
540 throw InconsistentDataException(e.getMessage() + getTransactionAuditDataAsString());
541 }
542}
543
544void CardTransactionManagerAdapter::parseApduResponses(
545 const std::vector<std::shared_ptr<AbstractCardCommand>>& commands,
546 const std::vector<std::shared_ptr<ApduResponseApi>>& apduResponses)
547{
548 /*
549 * If there are more responses than requests, then we are unable to fill the card image. In this
550 * case we stop processing immediately because it may be a case of fraud, and we throw a
551 * desynchronized exception.
552 */
553 if (apduResponses.size() > commands.size()) {
554
555 throw InconsistentDataException("The number of commands/responses does not match: nb " \
556 "commands = " +
557 std::to_string(commands.size()) +
558 ", nb responses = " +
559 std::to_string(apduResponses.size()));
560 }
561
562 /*
563 * We go through all the responses (and not the requests) because there may be fewer in the
564 * case of an error that occurred in strict mode. In this case the last response will raise an
565 * exception.
566 */
567 for (int i = 0; i < static_cast<int>(apduResponses.size()); i++) {
568
569 try {
570
571 commands[i]->parseApduResponse(apduResponses[i]);
572
573 } catch (const CardCommandException& e) {
574
575 const CalypsoCardCommand& commandRef = commands[i]->getCommandRef();
576
577 try {
578
579 (void)dynamic_cast<const CardDataAccessException&>(e);
580
581 if (commandRef == CalypsoCardCommand::READ_RECORDS ||
584 commandRef == CalypsoCardCommand::READ_BINARY) {
585
586 checkResponseStatusForStrictAndBestEffortMode(commands[i], e);
587
588 } else if (commandRef == CalypsoCardCommand::SELECT_FILE) {
589
590 throw SelectFileException("File not found",
591 std::make_shared<CardCommandException>(e));
592
593 } else {
594
595 throw UnexpectedCommandStatusException(
596 MSG_CARD_COMMAND_ERROR +
597 "while processing responses to card commands: " +
598 e.getCommand().getName() +
600 std::make_shared<CardCommandException>(e));
601 }
602
603 } catch (const std::bad_cast& ex) {
604
605 (void)ex;
606
607 throw UnexpectedCommandStatusException(
608 MSG_CARD_COMMAND_ERROR +
609 "while processing responses to card commands: " +
610 e.getCommand().getName() +
612 std::make_shared<CardCommandException>(e));
613 }
614 }
615 }
616
617 /*
618 * Finally, if no error has occurred and there are fewer responses than requests, then we
619 * throw a desynchronized exception.
620 */
621 if (apduResponses.size() < commands.size()) {
622
623 throw InconsistentDataException("The number of commands/responses does not match: nb " \
624 "commands = " +
625 std::to_string(commands.size()) +
626 ", nb responses = " +
627 std::to_string(apduResponses.size()));
628 }
629}
630
631void CardTransactionManagerAdapter::checkResponseStatusForStrictAndBestEffortMode(
632 const std::shared_ptr<AbstractCardCommand> command,
633 const CardCommandException& e) const
634{
635 if (mIsSessionOpen) {
636
637 throw e;
638
639 } else {
640
641 /*
642 * Best effort mode, do not throw exception for "file not found" and "record not found"
643 * errors.
644 */
645 if (command->getApduResponse()->getStatusWord() != 0x6A82 &&
646 command->getApduResponse()->getStatusWord() != 0x6A83) {
647
648 throw e;
649 }
650 }
651}
652
653void CardTransactionManagerAdapter::processAtomicClosing(
654 const std::vector<std::shared_ptr<AbstractApduCommand>>& cardCommands,
655 const bool isRatificationMechanismEnabled,
656 const ChannelControl channelControl)
657{
658 /* Get the list of C-APDU to transmit */
659 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests = getApduRequests(cardCommands);
660
661 /* Build the expected APDU respones of the card commands */
662 const std::vector<std::shared_ptr<ApduResponseApi>> expectedApduResponses =
663 buildAnticipatedResponses(cardCommands);
664
665 /* Add all commands data to the digest computation. */
666 mControlSamTransactionManager->updateSession(apduRequests, expectedApduResponses, 0);
667
668 /*
669 * All SAM digest operations will now run at once.
670 * Get Terminal Signature from the latest response.
671 */
672 const std::vector<uint8_t> sessionTerminalSignature = processSamSessionClosing();
673
674 /* Build the last "Close Secure Session" card command */
675 auto cmdCardCloseSession =
676 std::make_shared<CmdCardCloseSession>(mCard,
677 !isRatificationMechanismEnabled,
678 sessionTerminalSignature);
679
680 apduRequests.push_back(cmdCardCloseSession->getApduRequest());
681
682 /* Add the card Ratification command if any */
683 bool isRatificationCommandAdded;
684
685 if (isRatificationMechanismEnabled &&
686 std::dynamic_pointer_cast<CardReader>(mCardReader)->isContactless()) {
687
688 /*
689 * CL-RAT-CMD.1
690 * CL-RAT-DELAY.1
691 * CL-RAT-NXTCLOSE.1
692 */
693 apduRequests.push_back(
694 CmdCardRatificationBuilder::getApduRequest(mCard->getCardClass()));
695 isRatificationCommandAdded = true;
696
697 } else {
698
699 isRatificationCommandAdded = false;
700 }
701
702 /* Transfer card commands */
703 auto cardRequest = std::make_shared<CardRequestAdapter>(apduRequests, true);
704
705 /* Transmit the commands to the card */
706 std::shared_ptr<CardResponseApi> cardResponse;
707
708 try {
709
710 cardResponse = transmitCardRequest(cardRequest, channelControl);
711
712 } catch (const CardIOException& e) {
713
714 const auto cause = std::dynamic_pointer_cast<AbstractApduException>(e.getCause());
715 cardResponse = cause->getCardResponse();
716
717 /*
718 * The current exception may have been caused by a communication issue with the card
719 * during the ratification command.
720 * In this case, we do not stop the process and consider the Secure Session close. We'll
721 * check the signature.
722 * We should have one response less than requests.
723 */
724 if (!isRatificationCommandAdded ||
725 cardResponse == nullptr ||
726 cardResponse->getApduResponses().size() != apduRequests.size() - 1) {
727 throw e;
728 }
729 }
730
731 /* Retrieve the list of R-APDUs */
732 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses = cardResponse->getApduResponses();
733
734 /* Remove response of ratification command if present */
735 if (isRatificationCommandAdded && apduResponses.size() == cardCommands.size() + 2) {
736 apduResponses.pop_back();
737 }
738
739 /* Retrieve response of "Close Secure Session" command if present */
740 std::shared_ptr<ApduResponseApi> closeSecureSessionApduResponse = nullptr;
741 if (apduResponses.size() == cardCommands.size() + 1) {
742 closeSecureSessionApduResponse = apduResponses.back();
743 apduResponses.pop_back();
744 }
745
746 /*
747 * Check the commands executed before closing the secure session (only responses to these
748 * commands will be taken into account)
749 */
750 try {
751 /*
752 * C++: cannot cast std::vector<std::shared_ptr<derived>> into
753 * std::vector<std::shared_ptr<base>> automatically. Need to copy into another vector.
754 */
755 std::vector<std::shared_ptr<AbstractCardCommand>> copy;
756 for (int i = 0; i < static_cast<int>(cardCommands.size()); i++) {
757
758 copy.push_back(std::dynamic_pointer_cast<AbstractCardCommand>(cardCommands[i]));
759 }
760
761 parseApduResponses(copy, apduResponses);
762
763 } catch (const CardCommandException& e) {
764
765 throw UnexpectedCommandStatusException(MSG_CARD_COMMAND_ERROR +
766 "while processing of responses preceding the close" \
767 " of the session: " +
768 e.getCommand().getName() +
770 std::make_shared<CardCommandException>(e));
771
772 } catch (const InconsistentDataException& e) {
773
774 throw InconsistentDataException(e.getMessage() + getTransactionAuditDataAsString());
775 }
776
777
778 mIsSessionOpen = false;
779
780 /* Check the card's response to Close Secure Session */
781 try {
782
783 cmdCardCloseSession->parseApduResponse(closeSecureSessionApduResponse);
784
785 } catch (const CardSecurityDataException& e) {
786
787 throw UnexpectedCommandStatusException("Invalid card session" +
789 std::make_shared<CardSecurityDataException>(e));
790
791 } catch (const CardCommandException& e) {
792
793 throw UnexpectedCommandStatusException(MSG_CARD_COMMAND_ERROR +
794 "while processing the response to close session: " +
795 e.getCommand().getName() +
797 std::make_shared<CardCommandException>(e));
798 }
799
800 /*
801 * Check the card signature
802 * CL-CSS-MACVERIF.1
803 */
804 processSamDigestAuthenticate(cmdCardCloseSession->getSignatureLo());
805
806 /*
807 * If necessary, we check the status of the SV after the session has been successfully closed.
808 * CL-SV-POSTPON.1
809 */
811 processSamSvCheck(cmdCardCloseSession->getPostponedData()[mSvPostponedDataIndex]);
812 }
813}
814
815int CardTransactionManagerAdapter::getCounterValue(const uint8_t sfi, const int counter)
816{
817 const std::shared_ptr<ElementaryFile> ef = mCard->getFileBySfi(sfi);
818 if (ef != nullptr) {
819 const std::shared_ptr<int> counterValue = ef->getData()->getContentAsCounterValue(counter);
820 if (counterValue != nullptr) {
821 return *counterValue;
822 }
823 }
824
825 std::stringstream ss;
826 ss << "Anticipated response. Unable to determine anticipated value of counter "
827 << counter
828 << " in EF sfi "
829 << sfi;
830 throw IllegalStateException(ss.str());
831}
832
833const std::map<const int, const int> CardTransactionManagerAdapter::getCounterValues(
834 const uint8_t sfi, const std::vector<int>& counters)
835{
836 const std::shared_ptr<ElementaryFile> ef = mCard->getFileBySfi(sfi);
837 if (ef != nullptr) {
838 const std::map<const int, const int> allCountersValue = ef->getData()->getAllCountersValue();
839
840 if (Arrays::containsAll(MapUtils::getKeySet(allCountersValue), counters)) {
841 return allCountersValue;
842 }
843 }
844
845 std::stringstream ss;
846 ss << "Anticipated response. Unable to determine anticipated value of counters in EF sfi "
847 << sfi;
848 throw IllegalStateException(ss.str());
849}
850
851const std::vector<uint8_t>
852 CardTransactionManagerAdapter::buildAnticipatedIncreaseDecreaseResponseData(
853 const bool isDecreaseCommand,
854 const int currentCounterValue,
855 const int incDecValue)
856{
857 const int newValue = isDecreaseCommand ? currentCounterValue - incDecValue :
858 currentCounterValue + incDecValue;
859
860 return ByteArrayUtil::extractBytes(newValue, 3);
861}
862
863const std::shared_ptr<ApduResponseApi>
864 CardTransactionManagerAdapter::buildAnticipatedIncreaseDecreaseResponse(
865 const std::vector<uint8_t>& data)
866{
867 /* Response = NNNNNN9000 */
868 std::vector<uint8_t> response(5);
869 response[0] = data[0];
870 response[1] = data[1];
871 response[2] = data[2];
872 response[3] = 0x90;
873 response[4] = 0x00;
874
875 return std::make_shared<ApduResponseAdapter>(response);
876}
877
878const std::shared_ptr<ApduResponseApi>
879 CardTransactionManagerAdapter::buildAnticipatedIncreaseDecreaseMultipleResponse(
880 const bool isDecreaseCommand,
881 const std::map<const int, const int>& counterNumberToCurrentValueMap,
882 const std::map<const int, const int>& counterNumberToIncDecValueMap)
883{
884 /* Response = CCVVVVVV..CCVVVVVV9000 */
885 std::vector<uint8_t> response(2 + counterNumberToIncDecValueMap.size() * 4);
886 int index = 0;
887
888 for (const auto& entry : counterNumberToIncDecValueMap) {
889 response[index] = static_cast<uint8_t>(entry.first);
890 int newCounterValue;
891 if (isDecreaseCommand) {
892 const auto it = counterNumberToCurrentValueMap.find(entry.first);
893 newCounterValue = it->second - entry.second;
894 } else {
895 const auto it = counterNumberToCurrentValueMap.find(entry.first);
896 newCounterValue = it->second + entry.second;
897 }
898
899 ByteArrayUtil::copyBytes(newCounterValue, response, index + 1, 3);
900
901 index += 4;
902 }
903
904 response[index] = 0x90;
905 response[index + 1] = 0x00;
906
907 return std::make_shared<ApduResponseAdapter>(response);
908}
909
910const std::vector<std::shared_ptr<ApduResponseApi>>
911 CardTransactionManagerAdapter::buildAnticipatedResponses(
912 const std::vector<std::shared_ptr<AbstractApduCommand>>& cardCommands)
913{
914 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses;
915
916 if (!cardCommands.empty()) {
917
918 for (const auto& command : cardCommands) {
919
920 auto& commandRef = dynamic_cast<const CalypsoCardCommand&>(command->getCommandRef());
921 if (commandRef == CalypsoCardCommand::INCREASE ||
922 commandRef == CalypsoCardCommand::DECREASE) {
923
924 auto cmdA = std::dynamic_pointer_cast<CmdCardIncreaseOrDecrease>(command);
925
926 const std::vector<uint8_t> anticipatedValue =
927 buildAnticipatedIncreaseDecreaseResponseData(
928 cmdA->getCommandRef() == CalypsoCardCommand::DECREASE,
929 getCounterValue(cmdA->getSfi(), cmdA->getCounterNumber()),
930 cmdA->getIncDecValue());
931
932 if (mCard->isCounterValuePostponed()) {
933
934 cmdA->setComputedData(anticipatedValue);
935 apduResponses.push_back(RESPONSE_OK_POSTPONED);
936 mNbPostponedData++;
937
938 } else {
939
940 apduResponses.push_back(
941 buildAnticipatedIncreaseDecreaseResponse(anticipatedValue));
942 }
943
944 } else if (commandRef == CalypsoCardCommand::INCREASE_MULTIPLE ||
946
947 auto cmdB = std::dynamic_pointer_cast<CmdCardIncreaseOrDecreaseMultiple>(command);
948 const std::map<const int, const int>& counterNumberToIncDecValueMap =
949 cmdB->getCounterNumberToIncDecValueMap();
950 apduResponses.push_back(
951 buildAnticipatedIncreaseDecreaseMultipleResponse(
952 cmdB->getCommandRef() == CalypsoCardCommand::DECREASE_MULTIPLE,
953 getCounterValues(cmdB->getSfi(),
954 MapUtils::getKeySet(counterNumberToIncDecValueMap)),
955 counterNumberToIncDecValueMap));
956
957 } else if (commandRef == CalypsoCardCommand::SV_RELOAD ||
958 commandRef == CalypsoCardCommand::SV_DEBIT ||
959 commandRef == CalypsoCardCommand::SV_UNDEBIT) {
960
961 apduResponses.push_back(RESPONSE_OK_POSTPONED);
962 mSvPostponedDataIndex = mNbPostponedData;
963 mNbPostponedData++;
964
965 } else {
966
967 /* Append/Update/Write Record: response = 9000 */
968 apduResponses.push_back(RESPONSE_OK);
969 }
970 }
971 }
972
973 return apduResponses;
974}
975
977 const WriteAccessLevel writeAccessLevel)
978{
979 try {
980 checkNoSession();
981
982 /* CL-KEY-INDEXPO.1 */
983 mWriteAccessLevel = writeAccessLevel;
984
985 /* Create a sublist of AbstractCardCommand to be sent atomically */
986 std::vector<std::shared_ptr<AbstractApduCommand>> cardAtomicCommands;
987
988 for (const auto& apduCommand : mCardCommands) {
989
990 const auto& command = std::dynamic_pointer_cast<AbstractCardCommand>(apduCommand);
991 if (command->getCommandRef() == CalypsoCardCommand::GET_DATA
992 ||command->getCommandRef() == CalypsoCardCommand::READ_RECORD_MULTIPLE
993 ||command->getCommandRef() == CalypsoCardCommand::SEARCH_RECORD_MULTIPLE) {
994 throw IllegalStateException("Command not allowed in secure session.");
995 }
996 if (command->getCommandRef() == CalypsoCardCommand::READ_RECORDS
997 && std::dynamic_pointer_cast<CmdCardReadRecords>(command)->getSfi() != 0
998 && std::dynamic_pointer_cast<CmdCardReadRecords>(command)->getRecordSize() == -1) {
999 throw IllegalStateException("Explicit record size is expected inside a secure session.");
1000 }
1001 /* Check if the command is a modifying command */
1002 if (command->isSessionBufferUsed()) {
1003 mModificationsCounter -= computeCommandSessionBufferSize(command);
1004 if (mModificationsCounter < 0) {
1005 checkMultipleSessionEnabled(command);
1006
1007 /* Process and intermedisate secure session with the current commands */
1008 processAtomicOpening(cardAtomicCommands);
1009 std::vector<std::shared_ptr<AbstractApduCommand>> empty;
1010 processAtomicClosing(empty, false, ChannelControl::KEEP_OPEN);
1011
1012 /* Reset and update the buffer counter */
1013 mModificationsCounter = mCard->getModificationsCounter();
1014 mModificationsCounter -= computeCommandSessionBufferSize(command);
1015
1016 /* Clear the list */
1017 cardAtomicCommands.clear();
1018 }
1019 }
1020
1021 cardAtomicCommands.push_back(command);
1022 }
1023
1024 processAtomicOpening(cardAtomicCommands);
1025
1026 /* Sets the flag indicating that the commands have been executed */
1028
1029 /* CL-SV-1PCSS.1 */
1030 mIsSvOperationInsideSession = false;
1031
1032 return *this;
1033
1034 } catch (const RuntimeException& e) {
1035
1036 abortSecureSessionSilently();
1037 throw;
1038 }
1039}
1040
1041void CardTransactionManagerAdapter::checkMultipleSessionEnabled(
1042 std::shared_ptr<AbstractCardCommand> command) const
1043{
1044 /*
1045 * CL-CSS-REQUEST.1
1046 * CL-CSS-SMEXCEED.1
1047 * CL-CSS-INFOCSS.1
1048 */
1049 if (!mSecuritySetting->isMultipleSessionEnabled()) {
1050 throw SessionBufferOverflowException("ATOMIC mode error! This command would overflow the " \
1051 "card modifications buffer: " +
1052 command->getName() +
1054 }
1055}
1056
1057void CardTransactionManagerAdapter::processCommandsOutsideSession(
1058 const ChannelControl channelControl)
1059{
1060 /* Card commands sent outside a Secure Session. No modifications buffer limitation */
1061 processAtomicCardCommands(mCardCommands, channelControl);
1062
1063 /* Sets the flag indicating that the commands have been executed */
1065
1066 /* If an SV transaction was performed, we check the signature returned by the card here */
1068 /* Execute all prepared SAM commands and check SV status. */
1069 processSamSvCheck(mCard->getSvOperationSignature());
1070 } else {
1071 /* Execute all prepared SAM commands. */
1072 processSamPreparedCommands();
1073 }
1074}
1075
1076void CardTransactionManagerAdapter::processCommandsInsideSession()
1077{
1078 try {
1079 /* A session is open, we have to care about the card modifications buffer */
1080 std::vector<std::shared_ptr<AbstractApduCommand>> cardAtomicCommands;
1081 bool isAtLeastOneReadCommand = false;
1082
1083 for (const auto& apduCommand : mCardCommands) {
1084
1085 const auto& command = std::dynamic_pointer_cast<AbstractCardCommand>(apduCommand);
1086
1087 /* Check if the command is a modifying command */
1088 if (command->isSessionBufferUsed()) {
1089 mModificationsCounter -= computeCommandSessionBufferSize(command);
1090 if (mModificationsCounter < 0) {
1091 checkMultipleSessionEnabled(command);
1092
1093 /*
1094 * Close the current secure session with the current commands and open a new one
1095 */
1096 if (isAtLeastOneReadCommand) {
1097 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
1098 cardAtomicCommands.clear();
1099 }
1100
1101 processAtomicClosing(cardAtomicCommands, false, ChannelControl::KEEP_OPEN);
1102 std::vector<std::shared_ptr<AbstractApduCommand>> empty;
1103 processAtomicOpening(empty);
1104
1105 /* Reset and update the buffer counter */
1106 mModificationsCounter = mCard->getModificationsCounter();
1107 mModificationsCounter -= computeCommandSessionBufferSize(command);
1108 isAtLeastOneReadCommand = false;
1109
1110 /* Clear the list */
1111 cardAtomicCommands.clear();
1112 }
1113 } else {
1114 isAtLeastOneReadCommand = true;
1115 }
1116
1117 cardAtomicCommands.push_back(command);
1118 }
1119
1120 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
1121
1122 /* Sets the flag indicating that the commands have been executed */
1124
1125 processSamPreparedCommands();
1126
1127 } catch (const RuntimeException& e) {
1128 abortSecureSessionSilently();
1129 throw e;
1130 }
1131}
1132
1133const std::shared_ptr<CommonSecuritySetting> CardTransactionManagerAdapter::getSecuritySetting() const
1134{
1135 return mSecuritySetting;
1136}
1137
1138CardTransactionManager& CardTransactionManagerAdapter::prepareComputeSignature(const any data)
1139{
1140 checkControlSam();
1141
1142 mControlSamTransactionManager->prepareComputeSignature(data);
1143
1144 return *this;
1145}
1146
1147CardTransactionManager& CardTransactionManagerAdapter::prepareVerifySignature(const any data)
1148{
1149
1150 checkControlSam();
1151
1152 mControlSamTransactionManager->prepareVerifySignature(data);
1153
1154 return *this;
1155}
1156
1158{
1159 finalizeSvCommandIfNeeded();
1160
1161 if (mIsSessionOpen) {
1162 processCommandsInsideSession();
1163 } else {
1164 processCommandsOutsideSession(mChannelControl);
1165 }
1166
1167 return *this;
1168}
1169
1171{
1172 return processCommands();
1173}
1174
1176{
1177 try {
1178 checkSession();
1179 finalizeSvCommandIfNeeded();
1180
1181 std::vector<std::shared_ptr<AbstractApduCommand>> cardAtomicCommands;
1182 bool isAtLeastOneReadCommand = false;
1183
1184 for (const auto& apduCommand : mCardCommands) {
1185
1186 const auto& command = std::dynamic_pointer_cast<AbstractCardCommand>(apduCommand);
1187
1188 /* Check if the command is a modifying command */
1189 if (command->isSessionBufferUsed()) {
1190 mModificationsCounter -= computeCommandSessionBufferSize(command);
1191 if (mModificationsCounter < 0) {
1192 checkMultipleSessionEnabled(command);
1193
1194 /*
1195 * Close the current secure session with the current commands and open a new
1196 * one.
1197 */
1198 if (isAtLeastOneReadCommand) {
1199 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
1200 cardAtomicCommands.clear();
1201 }
1202
1203 processAtomicClosing(cardAtomicCommands, false, ChannelControl::KEEP_OPEN);
1204 std::vector<std::shared_ptr<AbstractApduCommand>> empty;
1205 processAtomicOpening(empty);
1206
1207 /* Reset and update the buffer counter */
1208 mModificationsCounter = mCard->getModificationsCounter();
1209 mModificationsCounter -= computeCommandSessionBufferSize(command);
1210 isAtLeastOneReadCommand = false;
1211
1212 /* Clear the list */
1213 cardAtomicCommands.clear();
1214 }
1215
1216 } else {
1217 isAtLeastOneReadCommand = true;
1218 }
1219
1220 cardAtomicCommands.push_back(command);
1221 }
1222
1223 if (isAtLeastOneReadCommand) {
1224 processAtomicCardCommands(cardAtomicCommands, ChannelControl::KEEP_OPEN);
1225 cardAtomicCommands.clear();
1226 }
1227
1228 processAtomicClosing(cardAtomicCommands,
1229 mSecuritySetting->isRatificationMechanismEnabled(),
1230 mChannelControl);
1231
1232 /* Sets the flag indicating that the commands have been executed */
1234
1235 return *this;
1236
1237 } catch (const RuntimeException& e) {
1238
1239 abortSecureSessionSilently();
1240 throw ;
1241 }
1242}
1243
1245{
1246 checkSession();
1247
1248 mCard->restoreFiles();
1249
1250 /* Build the card Close Session command (in "abort" mode since no signature is provided) */
1251 auto cmdCardCloseSession = std::make_shared<CmdCardCloseSession>(mCard);
1252
1253 /* Card ApduRequestAdapter List to hold close SecureSession command */
1254 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests;
1255 apduRequests.push_back(cmdCardCloseSession->getApduRequest());
1256
1257 /* Transfer card commands */
1258 const std::shared_ptr<CardRequestSpi> cardRequest =
1259 std::make_shared<CardRequestAdapter>(apduRequests, false);
1260 const std::shared_ptr<CardResponseApi> cardResponse =
1261 transmitCardRequest(cardRequest, mChannelControl);
1262
1263 try {
1264 cmdCardCloseSession->parseApduResponse(cardResponse->getApduResponses()[0]);
1265 } catch (const CardCommandException& e) {
1266 throw UnexpectedCommandStatusException(MSG_CARD_COMMAND_ERROR +
1267 "while processing the response to close session: " +
1268 e.getCommand().getName() +
1270 std::make_shared<CardCommandException>(e));
1271 }
1272
1273 /* Sets the flag indicating that the commands have been executed */
1275
1276 /*
1277 * Session is now considered closed regardless the previous state or the result of the abort
1278 * session command sent to the card.
1279 */
1280 mIsSessionOpen = false;
1281
1282 return *this;
1283}
1284
1286 const std::vector<uint8_t>& pin)
1287{
1288 try {
1289
1290 Assert::getInstance().isEqual(pin.size(), CalypsoCardConstant::PIN_LENGTH, "PIN length");
1291
1292 if (!mCard->isPinFeatureAvailable()) {
1293
1294 throw UnsupportedOperationException(MSG_PIN_NOT_AVAILABLE);
1295 }
1296
1297 if (!mCardCommands.empty()) {
1298
1299 throw IllegalStateException("No commands should have been prepared prior to a PIN " \
1300 "submission.");
1301 }
1302
1303 finalizeSvCommandIfNeeded();
1304
1305 /* CL-PIN-PENCRYPT.1 */
1306 if (mSecuritySetting != nullptr && !mSecuritySetting->isPinPlainTransmissionEnabled()) {
1307
1308 /* CL-PIN-GETCHAL.1 */
1309 mCardCommands.push_back(std::make_shared<CmdCardGetChallenge>(mCard));
1310
1311 /* Transmit and receive data with the card */
1312 processAtomicCardCommands(mCardCommands, ChannelControl::KEEP_OPEN);
1313
1314 /* Sets the flag indicating that the commands have been executed */
1316
1317 /* Get the encrypted PIN with the help of the SAM */
1318 std::vector<uint8_t> cipheredPin = processSamCardCipherPin(pin, std::vector<uint8_t>());
1319
1320 mCardCommands.push_back(std::make_shared<CmdCardVerifyPin>(mCard, true, cipheredPin));
1321
1322 } else {
1323
1324 mCardCommands.push_back(std::make_shared<CmdCardVerifyPin>(mCard, false, pin));
1325 }
1326
1327 /* Transmit and receive data with the card */
1328 processAtomicCardCommands(mCardCommands, mChannelControl);
1329
1330 /* Sets the flag indicating that the commands have been executed */
1332
1333 processSamPreparedCommands();
1334
1335 return *this;
1336
1337 } catch (const UnsupportedOperationException& e) {
1338
1339 /* C++: rethrow since we are in a try/catch block */
1340 (void)e;
1341 throw;
1342
1343 } catch (const IllegalStateException& e) {
1344
1345 /* C++: rethrow since we are in a try/catch block */
1346 (void)e;
1347 throw;
1348
1349 } catch (const RuntimeException& e) {
1350
1351 abortSecureSessionSilently();
1352 throw e;
1353
1354 }
1355}
1356
1357const std::vector<uint8_t> CardTransactionManagerAdapter::processSamCardCipherPin(
1358 const std::vector<uint8_t>& currentPin, const std::vector<uint8_t>& newPin)
1359{
1360 mControlSamTransactionManager->prepareGiveRandom();
1361 const std::shared_ptr<CmdSamCardCipherPin> cmdSamCardCipherPin =
1362 mControlSamTransactionManager->prepareCardCipherPin(currentPin, newPin);
1363 mControlSamTransactionManager->processCommands();
1364
1365 return cmdSamCardCipherPin->getCipheredData();
1366}
1367
1369 const std::vector<uint8_t>& newPin)
1370{
1371 try {
1372
1373 Assert::getInstance().isEqual(newPin.size(), CalypsoCardConstant::PIN_LENGTH, "PIN length");
1374
1375 if (!mCard->isPinFeatureAvailable()) {
1376
1377 throw UnsupportedOperationException(MSG_PIN_NOT_AVAILABLE);
1378 }
1379
1380 if (mIsSessionOpen) {
1381
1382 throw IllegalStateException("'Change PIN' not allowed when a secure session is open.");
1383 }
1384
1385 finalizeSvCommandIfNeeded();
1386
1387 /* CL-PIN-MENCRYPT.1 */
1388 if (mSecuritySetting->isPinPlainTransmissionEnabled()) {
1389
1390 /* Transmission in plain mode */
1391 if (mCard->getPinAttemptRemaining() >= 0) {
1392
1393 mCardCommands.push_back(std::make_shared<CmdCardChangePin>(mCard, newPin));
1394 }
1395
1396 } else {
1397
1398 /* CL-PIN-GETCHAL.1 */
1399 mCardCommands.push_back(std::make_shared<CmdCardGetChallenge>(mCard));
1400
1401 /* Transmit and receive data with the card */
1402 processAtomicCardCommands(mCardCommands, ChannelControl::KEEP_OPEN);
1403
1404 /* Sets the flag indicating that the commands have been executed */
1406
1407 /* Get the encrypted PIN with the help of the SAM */
1408 std::vector<uint8_t> currentPin(4); /* All zeros as required */
1409 std::vector<uint8_t> newPinData = processSamCardCipherPin(currentPin, newPin);
1410
1411 mCardCommands.push_back(std::make_shared<CmdCardChangePin>(mCard, newPinData));
1412 }
1413
1414 /* Transmit and receive data with the card */
1415 processAtomicCardCommands(mCardCommands, mChannelControl);
1416
1417 /* Sets the flag indicating that the commands have been executed */
1419
1420 processSamPreparedCommands();
1421
1422 return *this;
1423
1424 } catch (const RuntimeException& e) {
1425
1426 abortSecureSessionSilently();
1427 throw e;
1428 }
1429}
1430
1431CardTransactionManager& CardTransactionManagerAdapter::processChangeKey(const uint8_t keyIndex,
1432 const uint8_t newKif,
1433 const uint8_t newKvc,
1434 const uint8_t issuerKif,
1435 const uint8_t issuerKvc)
1436{
1437 if (mCard->getProductType() == CalypsoCard::ProductType::BASIC) {
1438
1439 throw UnsupportedOperationException("The 'Change Key' command is not available for this " \
1440 "card.");
1441 }
1442
1443 if (mIsSessionOpen) {
1444
1445 throw IllegalStateException("'Change Key' not allowed when a secure session is open.");
1446 }
1447
1448 Assert::getInstance().isInRange(keyIndex, 1, 3, "keyIndex");
1449
1450 finalizeSvCommandIfNeeded();
1451
1452 /* CL-KEY-CHANGE.1 */
1453 mCardCommands.push_back(std::make_shared<CmdCardGetChallenge>(mCard));
1454
1455 /* Transmit and receive data with the card */
1456 processAtomicCardCommands(mCardCommands, ChannelControl::KEEP_OPEN);
1457
1458 /* Sets the flag indicating that the commands have been executed */
1460
1461 /* Get the encrypted key with the help of the SAM */
1462 const std::vector<uint8_t> encryptedKey = processSamCardGenerateKey(issuerKif,
1463 issuerKvc,
1464 newKif,
1465 newKvc);
1466
1467 mCardCommands.push_back(std::make_shared<CmdCardChangeKey>(mCard, keyIndex, encryptedKey));
1468
1469 /* Transmit and receive data with the card */
1470 processAtomicCardCommands(mCardCommands, mChannelControl);
1471
1472 /* Sets the flag indicating that the commands have been executed */
1474
1475 return *this;
1476}
1477
1478const std::vector<uint8_t> CardTransactionManagerAdapter::processSamCardGenerateKey(
1479 const uint8_t issuerKif, const uint8_t issuerKvc, const uint8_t newKif, const uint8_t newKvc)
1480{
1481 mControlSamTransactionManager->prepareGiveRandom();
1482 const std::shared_ptr<CmdSamCardGenerateKey> cmdSamCardGenerateKey =
1483 mControlSamTransactionManager->prepareCardGenerateKey(issuerKif, issuerKvc, newKif, newKvc);
1484 mControlSamTransactionManager->processCommands();
1485
1486 return cmdSamCardGenerateKey->getCipheredData();
1487}
1488
1489const std::shared_ptr<CardResponseApi> CardTransactionManagerAdapter::transmitCardRequest(
1490 const std::shared_ptr<CardRequestSpi> cardRequest, const ChannelControl channelControl)
1491{
1492 /* Process card request */
1493 std::shared_ptr<CardResponseApi> cardResponse = nullptr;
1494
1495 try {
1496 cardResponse = mCardReader->transmitCardRequest(cardRequest, channelControl);
1497 } catch (const ReaderBrokenCommunicationException& e) {
1498 saveTransactionAuditData(cardRequest, e.getCardResponse());
1499 throw ReaderIOException(MSG_CARD_READER_COMMUNICATION_ERROR +
1502 std::make_shared<ReaderBrokenCommunicationException>(e));
1503 } catch (const CardBrokenCommunicationException& e) {
1504 saveTransactionAuditData(cardRequest, e.getCardResponse());
1505 throw CardIOException(MSG_CARD_COMMUNICATION_ERROR +
1508 std::make_shared<CardBrokenCommunicationException>(e));
1509 } catch (const UnexpectedStatusWordException& e) {
1510 mLogger->debug("A card command has failed: %\n", e.getMessage());
1511 cardResponse = e.getCardResponse();
1512 }
1513
1514 saveTransactionAuditData(cardRequest, cardResponse);
1515
1516 return cardResponse;
1517}
1518
1519void CardTransactionManagerAdapter::finalizeSvCommandIfNeeded()
1520{
1521 if (mSvLastModifyingCommand == nullptr) {
1522 return;
1523 }
1524
1525 std::vector<uint8_t> svComplementaryData;
1526
1527 if (mSvLastModifyingCommand->getCommandRef() == CalypsoCardCommand::SV_RELOAD) {
1528
1529 /* SV RELOAD: get the security data from the SAM. */
1530 auto svCommand = std::dynamic_pointer_cast<CmdCardSvReload>(mSvLastModifyingCommand);
1531
1532 svComplementaryData = processSamSvPrepareLoad(mCard->getSvGetHeader(),
1533 mCard->getSvGetData(),
1534 svCommand);
1535
1536 /* Finalize the SV command with the data provided by the SAM. */
1537 svCommand->finalizeCommand(svComplementaryData);
1538
1539 } else {
1540
1541 /* SV DEBIT/UNDEBIT: get the security data from the SAM. */
1542 auto svCommand = std::dynamic_pointer_cast<CmdCardSvDebitOrUndebit>(mSvLastModifyingCommand);
1543
1544 svComplementaryData = processSamSvPrepareDebitOrUndebit(
1545 svCommand->getCommandRef() == CalypsoCardCommand::SV_DEBIT,
1546 mCard->getSvGetHeader(),
1547 mCard->getSvGetData(),
1548 svCommand);
1549
1550 /* Finalize the SV command with the data provided by the SAM. */
1551 svCommand->finalizeCommand(svComplementaryData);
1552 }
1553}
1554
1555const std::vector<uint8_t> CardTransactionManagerAdapter::processSamSvPrepareLoad(
1556 const std::vector<uint8_t>& svGetHeader,
1557 const std::vector<uint8_t>& svGetData,
1558 const std::shared_ptr<CmdCardSvReload> cmdCardSvReload)
1559{
1560 const std::shared_ptr<CmdSamSvPrepareLoad> cmdSamSvPrepareLoad =
1561 mControlSamTransactionManager->prepareSvPrepareLoad(svGetHeader, svGetData, cmdCardSvReload);
1562 mControlSamTransactionManager->processCommands();
1563 const std::vector<uint8_t> prepareOperationData =
1564 cmdSamSvPrepareLoad->getApduResponse()->getDataOut();
1565
1566 return computeOperationComplementaryData(prepareOperationData);
1567}
1568
1569const std::vector<uint8_t> CardTransactionManagerAdapter::processSamSvPrepareDebitOrUndebit(
1570 const bool isDebitCommand,
1571 const std::vector<uint8_t> svGetHeader,
1572 const std::vector<uint8_t> svGetData,
1573 const std::shared_ptr<CmdCardSvDebitOrUndebit> cmdCardSvDebitOrUndebit)
1574{
1575 const std::shared_ptr<CmdSamSvPrepareDebitOrUndebit> cmdSamSvPrepareDebitOrUndebit =
1576 mControlSamTransactionManager->prepareSvPrepareDebitOrUndebit(isDebitCommand,
1577 svGetHeader,
1578 svGetData,
1579 cmdCardSvDebitOrUndebit);
1580 mControlSamTransactionManager->processCommands();
1581 const std::vector<uint8_t> prepareOperationData =
1582 cmdSamSvPrepareDebitOrUndebit->getApduResponse()->getDataOut();
1583
1584 return computeOperationComplementaryData(prepareOperationData);
1585}
1586
1587const std::vector<uint8_t> CardTransactionManagerAdapter::computeOperationComplementaryData(
1588 const std::vector<uint8_t>& prepareOperationData)
1589{
1590 const std::vector<uint8_t>& samSerialNumber =
1591 mSecuritySetting->getControlSam()->getSerialNumber();
1592 std::vector<uint8_t> operationComplementaryData(samSerialNumber.size() +
1593 prepareOperationData.size());
1594
1595 System::arraycopy(samSerialNumber, 0, operationComplementaryData, 0, samSerialNumber.size());
1596 System::arraycopy(prepareOperationData,
1597 0,
1598 operationComplementaryData,
1599 samSerialNumber.size(),
1600 prepareOperationData.size());
1601
1602 return operationComplementaryData;
1603}
1604
1605void CardTransactionManagerAdapter::processSamSvCheck(const std::vector<uint8_t>& svOperationData)
1606{
1607 mControlSamTransactionManager->prepareSvCheck(svOperationData);
1608
1609 try {
1610 mControlSamTransactionManager->processCommands();
1611 } catch (const ReaderIOException& e) {
1612 throw CardSignatureNotVerifiableException(MSG_CARD_SIGNATURE_NOT_VERIFIABLE_SV,
1613 std::make_shared<ReaderIOException>(e));
1614 } catch (const SamIOException& e) {
1615 throw CardSignatureNotVerifiableException(MSG_CARD_SIGNATURE_NOT_VERIFIABLE_SV,
1616 std::make_shared<SamIOException>(e));
1617 }
1618}
1619
1620const std::vector<uint8_t> CardTransactionManagerAdapter::processSamGetChallenge()
1621{
1622 const std::shared_ptr<CmdSamGetChallenge> cmdSamGetChallenge =
1623 mControlSamTransactionManager->prepareGetChallenge();
1624 mControlSamTransactionManager->processCommands();
1625 const std::vector<uint8_t> samChallenge = cmdSamGetChallenge->getChallenge();
1626
1627 mLogger->debug("SAM_CHALLENGE=%\n", HexUtil::toHex(samChallenge));
1628
1629 return samChallenge;
1630}
1631
1632const std::vector<uint8_t> CardTransactionManagerAdapter::processSamSessionClosing()
1633{
1634 const std::shared_ptr<CmdSamDigestClose> cmdSamDigestClose =
1635 mControlSamTransactionManager->prepareSessionClosing();
1636 mControlSamTransactionManager->processCommands();
1637 const std::vector<uint8_t> terminalSignature = cmdSamDigestClose->getSignature();
1638
1639 mLogger->debug("SAM_SIGNATURE=%\n", HexUtil::toHex(terminalSignature));
1640
1641 return terminalSignature;
1642}
1643
1644void CardTransactionManagerAdapter::processSamDigestAuthenticate(
1645 const std::vector<uint8_t>& cardSignature)
1646{
1647 mControlSamTransactionManager->prepareDigestAuthenticate(cardSignature);
1648
1649 try {
1650 mControlSamTransactionManager->processCommands();
1651 } catch (const ReaderIOException& e) {
1652 throw CardSignatureNotVerifiableException(MSG_CARD_SIGNATURE_NOT_VERIFIABLE,
1653 std::make_shared<ReaderIOException>(e));
1654 } catch (const SamIOException& e) {
1655 throw CardSignatureNotVerifiableException(MSG_CARD_SIGNATURE_NOT_VERIFIABLE,
1656 std::make_shared<SamIOException>(e));
1657 }
1658}
1659
1660void CardTransactionManagerAdapter::checkSession()
1661{
1662 if (!mIsSessionOpen) {
1663 throw IllegalStateException("No session is open");
1664 }
1665}
1666
1667void CardTransactionManagerAdapter::checkNoSession()
1668{
1669 if (mIsSessionOpen) {
1670 throw IllegalStateException("Session is open");
1671 }
1672}
1673
1674int CardTransactionManagerAdapter::computeCommandSessionBufferSize(
1675 std::shared_ptr<AbstractCardCommand> command)
1676{
1677 return mCard->isModificationsCounterInBytes() ?
1678 static_cast<int>(command->getApduRequest()->getApdu().size()) +
1679 SESSION_BUFFER_CMD_ADDITIONAL_COST -
1680 APDU_HEADER_LENGTH :
1681 1;
1682}
1683
1684
1685void CardTransactionManagerAdapter::resetModificationsBufferCounter()
1686{
1687 mLogger->trace("Modifications buffer counter reset: PREVIOUSVALUE = %, NEWVALUE = %\n",
1688 mModificationsCounter,
1689 mCard->getModificationsCounter());
1690
1691 mModificationsCounter = mCard->getModificationsCounter();
1692}
1693
1695{
1696 mChannelControl = ChannelControl::CLOSE_AFTER;
1697
1698 return *this;
1699}
1700
1702 const std::vector<uint8_t>& lid)
1703{
1704 Assert::getInstance().isEqual(lid.size(), 2, "lid length");
1705
1706 return prepareSelectFile(static_cast<uint16_t>(ByteArrayUtil::extractInt(lid, 0, 2, false)));
1707}
1708
1709CardTransactionManager& CardTransactionManagerAdapter::prepareSelectFile(const uint16_t lid)
1710{
1711 mCardCommands.push_back(std::make_shared<CmdCardSelectFile>(mCard, lid));
1712
1713 return *this;
1714}
1715
1717 const SelectFileControl selectFileControl)
1718{
1719 /* Create the command and add it to the list of commands */
1720 mCardCommands.push_back(std::make_shared<CmdCardSelectFile>(mCard, selectFileControl));
1721
1722 return *this;
1723}
1724
1725CardTransactionManager& CardTransactionManagerAdapter::prepareGetData(const GetDataTag tag)
1726{
1727 if (mIsSessionOpen) {
1728 throw IllegalStateException("Secure session open.");
1729 }
1730 /* Create the command and add it to the list of commands */
1731 switch (tag) {
1732
1733 case GetDataTag::FCI_FOR_CURRENT_DF:
1734 mCardCommands.push_back(std::make_shared<CmdCardGetDataFci>(mCard));
1735 break;
1736
1737 case GetDataTag::FCP_FOR_CURRENT_FILE:
1738 mCardCommands.push_back(std::make_shared<CmdCardGetDataFcp>(mCard));
1739 break;
1740
1741 case GetDataTag::EF_LIST:
1742 mCardCommands.push_back(std::make_shared<CmdCardGetDataEfList>(mCard));
1743 break;
1744
1745 case GetDataTag::TRACEABILITY_INFORMATION:
1746 mCardCommands.push_back(std::make_shared<CmdCardGetDataTraceabilityInformation>(mCard));
1747 break;
1748
1749 default:
1750 std::stringstream ss;
1751 ss << tag;
1752 throw UnsupportedOperationException("Unsupported Get Data tag: " + ss.str());
1753 }
1754
1755 return *this;
1756}
1757
1759 const uint8_t sfi, const uint8_t recordNumber)
1760{
1761 return prepareReadRecord(sfi, recordNumber);
1762}
1763
1765 const uint8_t sfi,
1766 const uint8_t firstRecordNumber,
1767 const uint8_t numberOfRecords,
1768 const uint8_t recordSize)
1769{
1770 return prepareReadRecords(sfi,
1771 firstRecordNumber,
1772 firstRecordNumber + numberOfRecords - 1,
1773 recordSize);
1774}
1775
1777 const uint8_t sfi, const uint8_t countersNumber)
1778{
1779 return prepareReadCounter(sfi, countersNumber);
1780}
1781
1783 const uint8_t sfi, const uint8_t recordNumber)
1784{
1785 Assert::getInstance().isInRange(sfi,
1788 "sfi")
1789 .isInRange(recordNumber,
1792 RECORD_NUMBER);
1793
1794 if (mIsSessionOpen) {
1795 throw IllegalStateException("Explicit record size is expected inside a secure session.");
1796 }
1797
1798 auto cmdCardReadRecords =
1799 std::make_shared<CmdCardReadRecords>(mCard,
1800 sfi,
1801 recordNumber,
1803 -1);
1804 mCardCommands.push_back(cmdCardReadRecords);
1805
1806 return *this;
1807}
1808
1810 const uint8_t sfi,
1811 const uint8_t fromRecordNumber,
1812 const uint8_t toRecordNumber,
1813 const uint8_t recordSize)
1814{
1815 Assert::getInstance().isInRange(sfi,
1818 "sfi")
1819 .isInRange(fromRecordNumber,
1822 "fromRecordNumber")
1823 .isInRange(toRecordNumber,
1824 fromRecordNumber,
1826 "toRecordNumber");
1827
1828 if (toRecordNumber == fromRecordNumber) {
1829
1830 /* Create the command and add it to the list of commands */
1831 mCardCommands.push_back(
1832 std::make_shared<CmdCardReadRecords>(mCard,
1833 sfi,
1834 fromRecordNumber,
1836 recordSize));
1837
1838 } else {
1839
1840 /*
1841 * Manages the reading of multiple records taking into account the transmission capacity
1842 * of the card and the response format (2 extra bytes).
1843 * Multiple APDUs can be generated depending on record size and transmission capacity.
1844 */
1845 const uint8_t nbBytesPerRecord = recordSize + 2;
1846 const uint8_t nbRecordsPerApdu =
1847 static_cast<uint8_t>(mCard->getPayloadCapacity() / nbBytesPerRecord);
1848 const uint8_t dataSizeMaxPerApdu = nbRecordsPerApdu * nbBytesPerRecord;
1849
1850 uint8_t currentRecordNumber = fromRecordNumber;
1851 uint8_t nbRecordsRemainingToRead = toRecordNumber - fromRecordNumber + 1;
1852 uint8_t currentLength;
1853
1854 while (currentRecordNumber < toRecordNumber) {
1855 currentLength = nbRecordsRemainingToRead <= nbRecordsPerApdu ?
1856 nbRecordsRemainingToRead * nbBytesPerRecord :
1857 dataSizeMaxPerApdu;
1858
1859 mCardCommands.push_back(
1860 std::make_shared<CmdCardReadRecords>(mCard,
1861 sfi,
1862 currentRecordNumber,
1864 currentLength));
1865
1866 currentRecordNumber += (currentLength / nbBytesPerRecord);
1867 nbRecordsRemainingToRead -= (currentLength / nbBytesPerRecord);
1868 }
1869
1870 /* Optimization: prepare a read "one record" if possible for last iteration.*/
1871 if (currentRecordNumber == toRecordNumber) {
1872
1873 mCardCommands.push_back(
1874 std::make_shared<CmdCardReadRecords>(mCard,
1875 sfi,
1876 currentRecordNumber,
1878 recordSize));
1879 }
1880 }
1881
1882 return *this;
1883}
1884
1886 const uint8_t sfi,
1887 const uint8_t fromRecordNumber,
1888 const uint8_t toRecordNumber,
1889 const uint8_t offset,
1890 const uint8_t nbBytesToRead)
1891{
1892 if (mCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3 &&
1893 mCard->getProductType() != CalypsoCard::ProductType::LIGHT) {
1894
1895 throw UnsupportedOperationException("The 'Read Record Multiple' command is not available "\
1896 "for this card.");
1897 }
1898 if (mIsSessionOpen) {
1899 throw IllegalStateException("Command not allowed inside a secure session.");
1900 }
1901
1902 Assert::getInstance().isInRange(sfi,
1905 "sfi")
1906 .isInRange(fromRecordNumber,
1909 "fromRecordNumber")
1910 .isInRange(toRecordNumber,
1911 fromRecordNumber,
1913 "toRecordNumber")
1914 .isInRange(offset,
1917 OFFSET)
1918 .isInRange(nbBytesToRead,
1921 "nbBytesToRead");
1922
1923 const uint8_t nbRecordsPerApdu =
1924 static_cast<uint8_t>(mCard->getPayloadCapacity() / nbBytesToRead);
1925
1926 uint8_t currentRecordNumber = fromRecordNumber;
1927
1928 while (currentRecordNumber <= toRecordNumber) {
1929
1930 mCardCommands.push_back(
1931 std::make_shared<CmdCardReadRecordMultiple>(mCard,
1932 sfi,
1933 currentRecordNumber,
1934 offset,
1935 nbBytesToRead));
1936 currentRecordNumber += nbRecordsPerApdu;
1937 }
1938
1939 return *this;
1940}
1941
1943 const uint8_t sfi, const int offset, const int nbBytesToRead)
1944{
1945 if (mCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3) {
1946
1947 if (mCard->getProductType() == CalypsoCard::ProductType::PRIME_REVISION_2) {
1948
1949 mLogger->warn("The 'Read Binary' command may not be supported by this " \
1950 "PRIME_REVISION_2 card\n");
1951 } else {
1952
1953 throw UnsupportedOperationException("The 'Read Binary' command is not available for " \
1954 "this card.");
1955 }
1956 }
1957
1958 Assert::getInstance().isInRange(sfi,
1961 "sfi")
1962 .isInRange(offset,
1965 OFFSET)
1966 .greaterOrEqual(nbBytesToRead, 1, "nbBytesToRead");
1967
1968 if (sfi > 0 && offset > 255) {
1969
1970 /* Tips to select the file: add a "Read Binary" command (read one byte at offset 0). */
1971 mCardCommands.push_back(std::make_shared<CmdCardReadBinary>(mCard, sfi, 0, 1));
1972 }
1973
1974 const int payloadCapacity = mCard->getPayloadCapacity();
1975
1976 int currentLength;
1977 int currentOffset = offset;
1978 int nbBytesRemainingToRead = nbBytesToRead;
1979
1980 do {
1981
1982 currentLength = std::min(nbBytesRemainingToRead, payloadCapacity);
1983 mCardCommands.push_back(
1984 std::make_shared<CmdCardReadBinary>(mCard, sfi, currentOffset, currentLength));
1985
1986 currentOffset += currentLength;
1987 nbBytesRemainingToRead -= currentLength;
1988
1989 } while (nbBytesRemainingToRead > 0);
1990
1991 return *this;
1992}
1993
1995 const uint8_t sfi, const uint8_t nbCountersToRead)
1996{
1997 return prepareReadRecords(sfi, 1, 1, nbCountersToRead * 3);
1998}
1999
2001 const std::shared_ptr<SearchCommandData> data)
2002{
2003 if (mCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3) {
2004
2005 throw UnsupportedOperationException("The 'Search Record Multiple' command is not " \
2006 "available for this card.");
2007 }
2008 if (mIsSessionOpen) {
2009 throw IllegalStateException("Command not allowed inside a secure session.");
2010 }
2011
2012 auto dataAdapter = std::dynamic_pointer_cast<SearchCommandDataAdapter>(data);
2013 if (!dataAdapter) {
2014
2015 throw IllegalArgumentException("The provided data must be an instance of " \
2016 "'SearchCommandDataAdapter'");
2017 }
2018
2019 Assert::getInstance().notNull(dataAdapter, "data")
2020 .isInRange(dataAdapter->getSfi(),
2023 "sfi")
2024 .isInRange(dataAdapter->getRecordNumber(),
2027 "startAtRecord")
2028 .isInRange(dataAdapter->getOffset(),
2031 OFFSET)
2032 .isInRange(dataAdapter->getSearchData().size(),
2034 CalypsoCardConstant::DATA_LENGTH_MAX - dataAdapter->getOffset(),
2035 "searchData");
2036 if (!dataAdapter->getMask().empty()) {
2037 Assert::getInstance().isInRange(dataAdapter->getMask().size(),
2039 dataAdapter->getSearchData().size(),
2040 "mask");
2041 }
2042
2043 mCardCommands.push_back(std::make_shared<CmdCardSearchRecordMultiple>(mCard, dataAdapter));
2044
2045 return *this;
2046}
2047
2049 const uint8_t sfi, const std::vector<uint8_t>& recordData)
2050{
2051 Assert::getInstance().isInRange(sfi,
2054 "sfi");
2055
2056 /* Create the command and add it to the list of commands */
2057 mCardCommands.push_back(std::make_shared<CmdCardAppendRecord>(mCard, sfi, recordData));
2058
2059 return *this;
2060}
2061
2063 const uint8_t sfi,
2064 const uint8_t recordNumber,
2065 const std::vector<uint8_t>& recordData)
2066{
2067 Assert::getInstance().isInRange(sfi,
2070 "sfi")
2071 .isInRange(recordNumber,
2074 RECORD_NUMBER);
2075
2076 /* Create the command and add it to the list of commands */
2077 mCardCommands.push_back(
2078 std::make_shared<CmdCardUpdateRecord>(mCard, sfi, recordNumber, recordData));
2079
2080 return *this;
2081}
2082
2084 const uint8_t sfi,
2085 const uint8_t recordNumber,
2086 const std::vector<uint8_t>& recordData)
2087{
2088 Assert::getInstance().isInRange(sfi,
2091 "sfi")
2092 .isInRange(recordNumber,
2095 RECORD_NUMBER);
2096
2097 /* Create the command and add it to the list of commands */
2098 mCardCommands.push_back(
2099 std::make_shared<CmdCardWriteRecord>(mCard, sfi, recordNumber, recordData));
2100
2101 return *this;
2102}
2103
2105 const uint8_t sfi,
2106 const int offset,
2107 const std::vector<uint8_t>& data)
2108{
2109 return prepareUpdateOrWriteBinary(true, sfi, offset, data);
2110}
2111
2113 const uint8_t sfi,
2114 const int offset,
2115 const std::vector<uint8_t>& data)
2116{
2117 return prepareUpdateOrWriteBinary(false, sfi, offset, data);
2118}
2119
2120CardTransactionManager& CardTransactionManagerAdapter::prepareUpdateOrWriteBinary(
2121 const bool isUpdateCommand,
2122 const uint8_t sfi,
2123 const int offset,
2124 const std::vector<uint8_t>& data)
2125{
2126 if (mCard->getProductType() != CalypsoCard::ProductType::PRIME_REVISION_3) {
2127
2128 if (mCard->getProductType() == CalypsoCard::ProductType::PRIME_REVISION_2) {
2129
2130 mLogger->warn("The 'Update/Write Binary' command may not be supported by this " \
2131 "PRIME_REVISION_2 card\n");
2132 } else {
2133
2134 throw UnsupportedOperationException("The 'Update/Write Binary' command is not " \
2135 "available for this card.");
2136 }
2137 }
2138
2139 Assert::getInstance().isInRange(sfi,
2142 "sfi")
2143 .isInRange(offset,
2146 OFFSET)
2147 .notEmpty(data, "data");
2148
2149 if (sfi > 0 && offset > 255) {
2150
2151 /* Tips to select the file: add a "Read Binary" command (read one byte at offset 0) */
2152 mCardCommands.push_back(std::make_shared<CmdCardReadBinary>(mCard, sfi, 0, 1));
2153 }
2154
2155 const uint8_t dataLength = static_cast<uint8_t>(data.size());
2156 const uint8_t payloadCapacity = mCard->getPayloadCapacity();
2157
2158 int currentLength;
2159 int currentOffset = offset;
2160 int currentIndex = 0;
2161
2162 do {
2163
2164 currentLength = static_cast<uint8_t>(
2165 std::min(static_cast<int>(dataLength - currentIndex),
2166 static_cast<int>(payloadCapacity)));
2167
2168 mCardCommands.push_back(
2169 std::make_shared<CmdCardUpdateOrWriteBinary>(
2170 isUpdateCommand,
2171 mCard,
2172 sfi,
2173 currentOffset,
2174 Arrays::copyOfRange(data, currentIndex, currentIndex + currentLength)));
2175
2176 currentOffset += currentLength;
2177 currentIndex += currentLength;
2178
2179 } while (currentIndex < dataLength);
2180
2181 return *this;
2182}
2183
2184CardTransactionManager& CardTransactionManagerAdapter::prepareIncreaseOrDecreaseCounter(
2185 const bool isDecreaseCommand,
2186 const uint8_t sfi,
2187 const uint8_t counterNumber,
2188 const int incDecValue)
2189{
2190 Assert::getInstance().isInRange(sfi,
2193 "sfi")
2194 .isInRange(counterNumber,
2197 "counterNumber")
2198 .isInRange(incDecValue,
2201 "incDecValue");
2202
2203 /* Create the command and add it to the list of commands */
2204 mCardCommands.push_back(std::make_shared<CmdCardIncreaseOrDecrease>(isDecreaseCommand,
2205 mCard,
2206 sfi,
2207 counterNumber,
2208 incDecValue));
2209
2210 return *this;
2211}
2212
2214 const uint8_t sfi, const uint8_t counterNumber, const int incValue)
2215{
2216 return prepareIncreaseOrDecreaseCounter(false, sfi, counterNumber, incValue);
2217}
2218
2220 const uint8_t sfi, const uint8_t counterNumber, const int decValue)
2221{
2222 return prepareIncreaseOrDecreaseCounter(true, sfi, counterNumber, decValue);
2223}
2224
2226 const uint8_t sfi,
2227 const std::map<const int, const int>& counterNumberToIncValueMap)
2228{
2229 return prepareIncreaseOrDecreaseCounters(false, sfi, counterNumberToIncValueMap);
2230}
2231
2233 const uint8_t sfi,
2234 const std::map<const int, const int>& counterNumberToDecValueMap)
2235{
2236 return prepareIncreaseOrDecreaseCounters(true, sfi, counterNumberToDecValueMap);
2237}
2238
2240{
2241 if (!mCard->isPinFeatureAvailable()) {
2242
2243 throw UnsupportedOperationException(MSG_PIN_NOT_AVAILABLE);
2244 }
2245
2246 /* Create the command and add it to the list of commands */
2247 mCardCommands.push_back(std::make_shared<CmdCardVerifyPin>(mCard));
2248
2249 return *this;
2250}
2251
2252CardTransactionManager& CardTransactionManagerAdapter::prepareSvGet(const SvOperation svOperation,
2253 const SvAction svAction)
2254{
2255 if (!mCard->isSvFeatureAvailable()) {
2256
2257 throw UnsupportedOperationException("Stored Value is not available for this card.");
2258 }
2259
2260 /* CL-SV-CMDMODE.1 */
2261 std::shared_ptr<CalypsoSam> calypsoSam = mSecuritySetting->getControlSam();
2262 const bool useExtendedMode = mCard->isExtendedModeSupported() &&
2263 (calypsoSam == nullptr ||
2264 calypsoSam->getProductType() == CalypsoSam::ProductType::SAM_C1 ||
2265 calypsoSam->getProductType() == CalypsoSam::ProductType::HSM_C1);
2266
2267 if (mSecuritySetting->isSvLoadAndDebitLogEnabled() && !useExtendedMode) {
2268
2269 /*
2270 * @see Calypso Layer ID 8.09/8.10 (200108): both reload and debit logs are requested
2271 * for a non rev3.2 card add two SvGet commands (for RELOAD then for DEBIT).
2272 * CL-SV-GETNUMBER.1
2273 */
2274 const SvOperation operation1 =
2275 SvOperation::RELOAD == svOperation ? SvOperation::DEBIT : SvOperation::RELOAD;
2276 addStoredValueCommand(std::make_shared<CmdCardSvGet>(mCard, operation1, false),
2277 operation1);
2278 }
2279
2280 addStoredValueCommand(std::make_shared<CmdCardSvGet>(mCard, svOperation, useExtendedMode),
2281 svOperation);
2282
2283 mSvAction = svAction;
2284
2285 return *this;
2286}
2287
2289 const int amount,
2290 const std::vector<uint8_t>& date,
2291 const std::vector<uint8_t>& time,
2292 const std::vector<uint8_t>& free)
2293{
2294 checkSvInsideSession();
2295
2296 /* Create the initial command with the application data */
2297 auto svReloadCmdBuild = std::make_shared<CmdCardSvReload>(mCard,
2298 amount,
2299 date,
2300 time,
2301 free,
2302 mIsSessionOpen,
2303 isExtendedModeAllowed());
2304
2305 /* Create and keep the CalypsoCardCommand */
2306 addStoredValueCommand(svReloadCmdBuild, SvOperation::RELOAD);
2307
2308 return *this;
2309}
2310
2311CardTransactionManager& CardTransactionManagerAdapter::prepareSvReload(const int amount)
2312{
2313 const std::vector<uint8_t> zero = {0x00, 0x00};
2314
2315 prepareSvReload(amount, zero, zero, zero);
2316
2317 return *this;
2318}
2319
2320void CardTransactionManagerAdapter::checkSvInsideSession()
2321{
2322 /* CL-SV-1PCSS.1 */
2323 if (mIsSessionOpen) {
2324 if (!mIsSvOperationInsideSession) {
2325 mIsSvOperationInsideSession = true;
2326 } else {
2327 throw IllegalStateException("Only one SV operation is allowed per Secure Session.");
2328 }
2329 }
2330}
2331
2332bool CardTransactionManagerAdapter::isExtendedModeAllowed() const
2333{
2334 std::shared_ptr<CalypsoSam> calypsoSam = mSecuritySetting->getControlSam();
2335
2336 return mCard->isExtendedModeSupported() &&
2337 (calypsoSam->getProductType() == CalypsoSam::ProductType::SAM_C1 ||
2338 calypsoSam->getProductType() == CalypsoSam::ProductType::HSM_C1);
2339}
2340
2342 const int amount,
2343 const std::vector<uint8_t>& date,
2344 const std::vector<uint8_t>& time)
2345{
2346 checkSvInsideSession();
2347
2348 if (mSvAction == SvAction::DO &&
2349 !mSecuritySetting->isSvNegativeBalanceAuthorized() &&
2350 (mCard->getSvBalance() - amount) < 0) {
2351 throw IllegalStateException("Negative balances not allowed.");
2352 }
2353
2354 /* Create the initial command with the application data */
2355 auto command = std::make_shared<CmdCardSvDebitOrUndebit>(mSvAction == SvAction::DO,
2356 mCard,
2357 amount,
2358 date,
2359 time,
2360 mIsSessionOpen,
2361 isExtendedModeAllowed());
2362
2363 /* Create and keep the CalypsoCardCommand */
2364 addStoredValueCommand(command, SvOperation::DEBIT);
2365
2366 return *this;
2367 }
2368
2369CardTransactionManager& CardTransactionManagerAdapter::prepareSvDebit(const int amount)
2370{
2371 const std::vector<uint8_t> zero = {0x00, 0x00};
2372
2373 prepareSvDebit(amount, zero, zero);
2374
2375 return *this;
2376}
2377
2379{
2380 if (!mCard->isSvFeatureAvailable()) {
2381 throw UnsupportedOperationException("Stored Value is not available for this card.");
2382 }
2383
2384 if (mCard->getApplicationSubtype() !=
2386 throw UnsupportedOperationException("The currently selected application is not an SV " \
2387 "application.");
2388 }
2389
2390 /* Reset SV data in CalypsoCard if any */
2391 const std::vector<uint8_t> dummy;
2392 mCard->setSvData(0, dummy, dummy, 0, 0, nullptr, nullptr);
2394 1,
2398 1,
2401
2402 return *this;
2403}
2404
2406{
2407 if (mCard->isDfInvalidated()) {
2408 throw IllegalStateException("This card is already invalidated.");
2409 }
2410
2411 mCardCommands.push_back(std::make_shared<CmdCardInvalidate>(mCard));
2412
2413 return *this;
2414}
2415
2417{
2418 if (!mCard->isDfInvalidated()) {
2419 throw IllegalStateException("This card is not invalidated.");
2420 }
2421
2422 mCardCommands.push_back(std::make_shared<CmdCardRehabilitate>(mCard));
2423
2424 return *this;
2425}
2426
2428 const std::shared_ptr<AbstractCardCommand> command, const SvOperation svOperation)
2429{
2430 /* Check the logic of the SV command sequencing */
2431 if (command->getCommandRef() == CalypsoCardCommand::SV_GET) {
2432 mSvOperation = svOperation;
2433
2434 } else if (command->getCommandRef() == CalypsoCardCommand::SV_RELOAD ||
2435 command->getCommandRef() == CalypsoCardCommand::SV_DEBIT ||
2436 command->getCommandRef() == CalypsoCardCommand::SV_UNDEBIT) {
2437 /*
2438 * CL-SV-GETDEBIT.1
2439 * CL-SV-GETRLOAD.1
2440 */
2441 if (!mCardCommands.empty()) {
2442 throw IllegalStateException("This SV command can only be placed in the first position" \
2443 " in the list of prepared commands");
2444 }
2445
2446 if (mSvLastCommandRef != CalypsoCardCommand::SV_GET) {
2447 throw IllegalStateException("This SV command must follow an SV Get command");
2448 }
2449
2450 /* Here, we expect the command and the SV operation to be consistent */
2451 if (svOperation != mSvOperation) {
2452 mLogger->error("Sv operation = %, current command = %\n", mSvOperation, svOperation);
2453 throw IllegalStateException("Inconsistent SV operation.");
2454 }
2455
2456 mIsSvOperationComplete = true;
2457 mSvLastModifyingCommand = command;
2458
2459 } else {
2460 throw IllegalStateException("An SV command is expected.");
2461 }
2462
2463 mSvLastCommandRef = command->getCommandRef();
2464 mCardCommands.push_back(command);
2465}
2466
2468{
2469 mCardCommands.clear();
2470 mSvLastModifyingCommand = nullptr;
2471}
2472
2474{
2475 const bool flag = mIsSvOperationComplete;
2476 mIsSvOperationComplete = false;
2477
2478 return flag;
2479}
2480
2481/* APDU RESPONSE ADAPTER ------------------------------------------------------------------------ */
2482
2483CardTransactionManagerAdapter::ApduResponseAdapter::ApduResponseAdapter(
2484 const std::vector<uint8_t>& apdu)
2485: mApdu(apdu),
2486 mStatusWord(ByteArrayUtil::extractInt(apdu, apdu.size() - 2, 2, false)) {}
2487
2488const std::vector<uint8_t>& CardTransactionManagerAdapter::ApduResponseAdapter::getApdu() const
2489{
2490 return mApdu;
2491}
2492
2493const std::vector<uint8_t> CardTransactionManagerAdapter::ApduResponseAdapter::getDataOut() const
2494{
2495 return Arrays::copyOfRange(mApdu, 0, mApdu.size() - 2);
2496}
2497
2498int CardTransactionManagerAdapter::ApduResponseAdapter::getStatusWord() const
2499{
2500 return mStatusWord;
2501}
2502
2503std::ostream& operator<<(std::ostream& os, const CardTransactionManagerAdapter::ApduResponseAdapter& ara)
2504{
2505 os << "APDU_RESPONSE_ADAPTER: {"
2506 << "APDU: " << ara.getApdu() << ", "
2507 << "STATUS_WORD: " << ara.getStatusWord()
2508 << "}";
2509
2510 return os;
2511}
2512
2513
2514std::ostream& operator<<(std::ostream& os, const std::shared_ptr<CardTransactionManagerAdapter::ApduResponseAdapter> ara)
2515{
2516 if (ara == nullptr) {
2517 os << "APDU_RESPONSE_ADAPTER: null";
2518 } else {
2519 os << *ara;
2520 }
2521
2522 return os;
2523}
2524
2525}
2526}
2527}
static const CalypsoCardCommand DECREASE
static const CalypsoCardCommand SV_RELOAD
static const CalypsoCardCommand INCREASE
static const CalypsoCardCommand SV_DEBIT
static const CalypsoCardCommand READ_RECORD_MULTIPLE
static const CalypsoCardCommand SELECT_FILE
static const CalypsoCardCommand GET_DATA
static const CalypsoCardCommand READ_RECORDS
static const CalypsoCardCommand READ_BINARY
static const CalypsoCardCommand SEARCH_RECORD_MULTIPLE
static const CalypsoCardCommand DECREASE_MULTIPLE
static const CalypsoCardCommand SV_GET
static const CalypsoCardCommand SV_UNDEBIT
static const CalypsoCardCommand INCREASE_MULTIPLE
virtual const std::string & getName() const =0
const std::shared_ptr< CalypsoCard > getCalypsoCard() const override
const std::vector< std::vector< uint8_t > > & getTransactionAuditData() const final
CardTransactionManager & processChangePin(const std::vector< uint8_t > &newPin) override
CardTransactionManager & prepareWriteRecord(const uint8_t sfi, const uint8_t recordNumber, const std::vector< uint8_t > &recordData) override
CardTransactionManager & prepareAppendRecord(const uint8_t sfi, const std::vector< uint8_t > &recordData) override
CardTransactionManager & prepareSvGet(const SvOperation svOperation, const SvAction svAction) override
const std::shared_ptr< CardSecuritySetting > getCardSecuritySetting() const override
CardTransactionManager & prepareIncreaseCounter(const uint8_t sfi, const uint8_t counterNumber, const int incValue) override
CardTransactionManagerAdapter(const std::shared_ptr< ProxyReaderApi > cardReader, const std::shared_ptr< CalypsoCardAdapter > card, const std::shared_ptr< CardSecuritySettingAdapter > securitySetting)
CardTransactionManager & prepareUpdateBinary(const uint8_t sfi, const int offset, const std::vector< uint8_t > &data) final
CardTransactionManager & prepareReadRecord(const uint8_t sfi, const uint8_t recordNumber) override
CardTransactionManager & prepareWriteBinary(const uint8_t sfi, const int offset, const std::vector< uint8_t > &data) final
CardTransactionManager & prepareVerifySignature(const any data) override
CardTransactionManager & processOpening(const WriteAccessLevel writeAccessLevel) override
CardTransactionManager & processVerifyPin(const std::vector< uint8_t > &pin) override
CardTransactionManager & processChangeKey(const uint8_t keyIndex, const uint8_t newKif, const uint8_t newKvc, const uint8_t issuerKif, const uint8_t issuerKvc) override
CardTransactionManager & prepareReadBinary(const uint8_t sfi, const int offset, const int nbBytesToRead) override
CardTransactionManager & prepareReadRecords(const uint8_t sfi, const uint8_t fromRecordNumber, const uint8_t toRecordNumber, const uint8_t recordSize) override
const std::shared_ptr< CardReader > getCardReader() const override
CardTransactionManager & prepareGetData(const GetDataTag tag) override
CardTransactionManager & prepareDecreaseCounter(const uint8_t sfi, const uint8_t counterNumber, const int decValue) override
CardTransactionManager & prepareIncreaseCounters(const uint8_t sfi, const std::map< const int, const int > &counterNumberToIncValueMap) override
CardTransactionManager & prepareSvReload(const int amount, const std::vector< uint8_t > &date, const std::vector< uint8_t > &time, const std::vector< uint8_t > &free) override
CardTransactionManager & prepareReadCounter(const uint8_t sfi, const uint8_t nbCountersToRead) override
CardTransactionManager & prepareReadRecordsPartially(const uint8_t sfi, const uint8_t fromRecordNumber, const uint8_t toRecordNumber, const uint8_t offset, const uint8_t nbBytesToRead) override
CardTransactionManager & prepareSetCounter(const uint8_t sfi, const uint8_t counterNumber, const int newValue) override
CardTransactionManager & prepareComputeSignature(const any data) override
CardTransactionManager & prepareSvDebit(const int amount, const std::vector< uint8_t > &date, const std::vector< uint8_t > &time) override
void addStoredValueCommand(const std::shared_ptr< AbstractCardCommand > command, const SvOperation svOperation)
CardTransactionManager & prepareDecreaseCounters(const uint8_t sfi, const std::map< const int, const int > &counterNumberToDecValueMap) override
CardTransactionManager & prepareReadCounterFile(const uint8_t sfi, const uint8_t countersNumber) override
CardTransactionManager & prepareSearchRecords(const std::shared_ptr< SearchCommandData > data) override
const std::shared_ptr< CommonSecuritySetting > getSecuritySetting() const override
CardTransactionManager & prepareSelectFile(const std::vector< uint8_t > &lid) override
CardTransactionManager & prepareUpdateRecord(const uint8_t sfi, const uint8_t recordNumber, const std::vector< uint8_t > &recordData) override
CardTransactionManager & prepareReadRecordFile(const uint8_t sfi, const uint8_t recordNumber) override
static const std::shared_ptr< ApduRequestAdapter > getApduRequest(const CalypsoCardClass calypsoCardClass)
virtual void saveTransactionAuditData(const std::shared_ptr< CardRequestSpi > cardRequest, const std::shared_ptr< CardResponseApi > cardResponse)
const std::vector< std::shared_ptr< ApduRequestSpi > > getApduRequests(const std::vector< std::shared_ptr< AbstractApduCommand > > &commands)
std::ostream & operator<<(std::ostream &os, const std::shared_ptr< ApduRequestAdapter > ara)