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