16#include "DesynchronizedExchangesException.h"
20#include "ChannelControl.h"
21#include "UnexpectedStatusWordException.h"
24#include "ApduRequestSpi.h"
45#include "ByteArrayUtil.h"
46#include "IllegalStateException.h"
47#include "KeypleAssert.h"
54using namespace calypsonet::terminal::calypso::transaction;
55using namespace calypsonet::terminal::card;
56using namespace calypsonet::terminal::card::spi;
57using namespace keyple::core::util;
58using namespace keyple::core::util::cpp;
59using namespace keyple::core::util::cpp::exception;
61const uint8_t SamCommandProcessor::KIF_UNDEFINED = 0xFF;
62const uint8_t SamCommandProcessor::CHALLENGE_LENGTH_REV_INF_32 = 0x04;
63const uint8_t SamCommandProcessor::CHALLENGE_LENGTH_REV32 = 0x08;
64const uint8_t SamCommandProcessor::SIGNATURE_LENGTH_REV_INF_32 = 0x04;
65const uint8_t SamCommandProcessor::SIGNATURE_LENGTH_REV32 = 0x08;
66const std::string SamCommandProcessor::UNEXPECTED_EXCEPTION =
"An unexpected exception was raised.";
67std::vector<std::vector<uint8_t>> SamCommandProcessor::mCardDigestDataCache;
70 const std::shared_ptr<CalypsoCard> calypsoCard,
71 const std::shared_ptr<CardSecuritySetting> cardSecuritySetting)
72: mCardSecuritySettings(cardSecuritySetting),
74 mIsDiversificationDone(false)
76 const auto stngs = std::dynamic_pointer_cast<CardSecuritySettingAdapter>(cardSecuritySetting);
77 Assert::getInstance().notNull(stngs,
"securitySettings")
78 .notNull(stngs->getSamReader(),
"samReader")
79 .notNull(stngs->getCalypsoSam(),
"calypsoSam");
81 const auto calypsoSam = stngs->getCalypsoSam();
82 mSamProductType = calypsoSam->getProductType();
83 mSamSerialNumber = calypsoSam->getSerialNumber();
84 mSamReader = std::dynamic_pointer_cast<ProxyReaderApi>(stngs->getSamReader());
89 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests;
92 if (!mIsDiversificationDone) {
97 const auto selectDiversifierCmd =
98 std::make_shared<CmdSamSelectDiversifier>(mSamProductType,
99 mCalypsoCard->getCalypsoSerialNumberFull());
101 apduRequests.push_back(selectDiversifierCmd->getApduRequest());
104 mIsDiversificationDone =
true;
108 const uint8_t challengeLength = mCalypsoCard->isExtendedModeSupported() ?
109 CHALLENGE_LENGTH_REV32 : CHALLENGE_LENGTH_REV_INF_32;
111 auto samGetChallengeCmd = std::make_shared<CmdSamGetChallenge>(mSamProductType,challengeLength);
113 apduRequests.push_back(samGetChallengeCmd->getApduRequest());
116 std::shared_ptr<CardResponseApi> samCardResponse;
118 samCardResponse = mSamReader->transmitCardRequest(
119 std::make_shared<CardRequestAdapter>(apduRequests,
false),
120 ChannelControl::KEEP_OPEN);
121 }
catch (
const UnexpectedStatusWordException& e) {
122 throw IllegalStateException(UNEXPECTED_EXCEPTION,
123 std::make_shared<UnexpectedStatusWordException>(e));
126 const std::vector<std::shared_ptr<ApduResponseApi>>&
127 samApduResponses = samCardResponse->getApduResponses();
128 std::vector<uint8_t> sessionTerminalChallenge;
130 const size_t numberOfSamCmd = apduRequests.size();
131 if (samApduResponses.size() == numberOfSamCmd) {
132 samGetChallengeCmd->setApduResponse(samApduResponses[numberOfSamCmd - 1]).checkStatus();
133 sessionTerminalChallenge = samGetChallengeCmd->getChallenge();
134 mLogger->debug(
"identification: TERMINALCHALLENGE = %\n",
135 ByteArrayUtil::toHex(sessionTerminalChallenge));
138 throw DesynchronizedExchangesException(
"The number of commands/responses does not match: " \
139 "cmd=" + std::to_string(numberOfSamCmd) +
", " +
140 "resp=" + std::to_string(samApduResponses.size()));
143 return sessionTerminalChallenge;
147 const WriteAccessLevel writeAccessLevel,
const std::shared_ptr<uint8_t> kvc)
const
149 if (kvc !=
nullptr) {
153 return std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings)
154 ->getDefaultKvc(writeAccessLevel);
158 const WriteAccessLevel writeAccessLevel,
159 const std::shared_ptr<uint8_t> kif,
160 const std::shared_ptr<uint8_t> kvc)
163 if ((kif !=
nullptr && *kif.get() != KIF_UNDEFINED) || (kvc ==
nullptr)) {
168 const auto adptr = std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings);
169 std::shared_ptr<uint8_t> result = adptr->getKif(writeAccessLevel, *kvc.get());
170 if (result ==
nullptr) {
171 result = adptr->getDefaultKif(writeAccessLevel);
178 const bool verificationMode,
181 const std::vector<uint8_t>& digestData)
183 mSessionEncryption = sessionEncryption;
184 mVerificationMode = verificationMode;
188 mLogger->debug(
"initialize: POREVISION = %, SAMREVISION = %, SESSIONENCRYPTION = %, " \
189 "VERIFICATIONMODE = %\n",
190 mCalypsoCard->getProductType(),
194 mLogger->debug(
"initialize: VERIFICATIONMODE = %, REV32MODE = %\n",
196 mCalypsoCard->isExtendedModeSupported());
197 mLogger->debug(
"initialize: KIF = %, KVC %, DIGESTDATA = %\n",
200 ByteArrayUtil::toHex(digestData));
203 mCardDigestDataCache.clear();
206 mCardDigestDataCache.push_back(digestData);
208 mIsDigestInitDone =
false;
209 mIsDigesterInitialized =
true;
213 const std::shared_ptr<ApduResponseApi> response)
215 mLogger->trace(
"pushCardExchangedData: %\n", request);
223 if (ApduUtil::isCase4(request->getApdu())) {
224 mCardDigestDataCache.push_back(
225 Arrays::copyOfRange(request->getApdu(), 0, request->getApdu().size() - 1));
227 mCardDigestDataCache.push_back(request->getApdu());
230 mLogger->trace(
"pushCardExchangedData: %\n", response);
233 mCardDigestDataCache.push_back(response->getApdu());
237 const std::vector<std::shared_ptr<ApduRequestSpi>>& requests,
238 const std::vector<std::shared_ptr<ApduResponseApi>>& responses,
239 const int startIndex)
241 for (
int i = startIndex; i < static_cast<int>(requests.size()); i++) {
247const std::vector<std::shared_ptr<AbstractSamCommand>> SamCommandProcessor::getPendingSamCommands(
248 const bool addDigestClose)
251 std::vector<std::shared_ptr<AbstractSamCommand>> samCommands;
254 if (mCardDigestDataCache.empty()) {
255 mLogger->debug(
"getSamDigestRequest: no data in cache\n");
256 throw IllegalStateException(
"Digest data cache is empty.");
259 if (!mIsDigestInitDone && mCardDigestDataCache.size() % 2 == 0) {
261 mLogger->debug(
"getSamDigestRequest: wrong number of buffer in cache NBR = %\n",
262 mCardDigestDataCache.size());
263 throw IllegalStateException(
"Digest data cache is inconsistent.");
266 if (!mIsDigestInitDone) {
274 samCommands.push_back(
275 std::make_shared<CmdSamDigestInit>(mSamProductType,
277 mCalypsoCard->isExtendedModeSupported(),
280 mCardDigestDataCache[0]));
281 mCardDigestDataCache.erase(mCardDigestDataCache.begin());
284 mIsDigestInitDone =
true;
291 for (
const auto& bytes : mCardDigestDataCache) {
292 samCommands.push_back(
293 std::make_shared<CmdSamDigestUpdate>(mSamProductType, mSessionEncryption, bytes));
297 mCardDigestDataCache.clear();
299 if (addDigestClose) {
304 samCommands.push_back(
305 std::make_shared<CmdSamDigestClose>(mSamProductType,
306 mCalypsoCard->isExtendedModeSupported() ?
307 SIGNATURE_LENGTH_REV32 :
308 SIGNATURE_LENGTH_REV_INF_32));
320 std::vector<std::shared_ptr<AbstractSamCommand>> samCommands = getPendingSamCommands(
true);
322 auto samCardRequest = std::make_shared<CardRequestAdapter>(getApduRequests(samCommands),
false);
325 std::shared_ptr<CardResponseApi> samCardResponse;
328 samCardResponse = mSamReader->transmitCardRequest(samCardRequest,
329 ChannelControl::KEEP_OPEN);
330 }
catch (
const UnexpectedStatusWordException& e) {
331 throw IllegalStateException(UNEXPECTED_EXCEPTION,
332 std::make_shared<UnexpectedStatusWordException>(e));
335 std::vector<std::shared_ptr<ApduResponseApi>> samApduResponses =
336 samCardResponse->getApduResponses();
338 if (samApduResponses.size() != samCommands.size()) {
339 throw DesynchronizedExchangesException(
"The number of commands/responses does not match: " \
340 "cmd=" + std::to_string(samCommands.size()) +
", " +
341 "resp="+ std::to_string(samApduResponses.size()));
345 for (
int i = 0; i < static_cast<int>(samApduResponses.size()); i++) {
346 samCommands[i]->setApduResponse(samApduResponses[i]).checkStatus();
350 auto cmdSamDigestClose = std::dynamic_pointer_cast<CmdSamDigestClose>(samCommands.back());
351 cmdSamDigestClose->setApduResponse(samApduResponses[samCommands.size() - 1]);
353 const std::vector<uint8_t> sessionTerminalSignature = cmdSamDigestClose->getSignature();
355 mLogger->debug(
"SIGNATURE = %\n", ByteArrayUtil::toHex(sessionTerminalSignature));
357 return sessionTerminalSignature;
360const std::vector<std::shared_ptr<ApduRequestSpi>> SamCommandProcessor::getApduRequests(
361 const std::vector<std::shared_ptr<AbstractSamCommand>> samCommands)
const
363 std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests;
365 if (!samCommands.empty()) {
366 for (
const auto& samCommand : samCommands) {
367 apduRequests.push_back(samCommand->getApduRequest());
380 auto cmdSamDigestAuthenticate = std::make_shared<CmdSamDigestAuthenticate>(mSamProductType,
383 std::vector<std::shared_ptr<ApduRequestSpi>> samApduRequests;
384 samApduRequests.push_back(cmdSamDigestAuthenticate->getApduRequest());
386 auto samCardRequest = std::dynamic_pointer_cast<CardRequestSpi>(
387 std::make_shared<CardRequestAdapter>(samApduRequests,
false));
389 std::shared_ptr<CardResponseApi> samCardResponse;
391 samCardResponse = mSamReader->transmitCardRequest(samCardRequest,
392 ChannelControl::KEEP_OPEN);
393 }
catch (
const UnexpectedStatusWordException& e) {
394 throw IllegalStateException(UNEXPECTED_EXCEPTION,
395 std::make_shared<UnexpectedStatusWordException>(e));
399 std::vector<std::shared_ptr<ApduResponseApi>> samApduResponses =
400 samCardResponse->getApduResponses();
402 if (samApduResponses.empty()) {
403 throw DesynchronizedExchangesException(
"No response to Digest Authenticate command.");
406 cmdSamDigestAuthenticate->setApduResponse(samApduResponses[0]).checkStatus();
410 const std::vector<uint8_t>& poChallenge,
411 const uint8_t cipheringKif,
412 const uint8_t cipheringKvc,
413 const uint8_t sourceKif,
414 const uint8_t sourceKvc)
416 std::vector<std::shared_ptr<AbstractSamCommand>> samCommands;
418 if (!mIsDiversificationDone) {
423 samCommands.push_back(
424 std::make_shared<CmdSamSelectDiversifier>(mSamProductType,
425 mCalypsoCard->getCalypsoSerialNumberFull()));
426 mIsDiversificationDone =
true;
429 samCommands.push_back(std::make_shared<CmdSamGiveRandom>(mSamProductType, poChallenge));
431 const size_t cardGenerateKeyCmdIndex = samCommands.size();
433 auto cmdSamCardGenerateKey = std::make_shared<CmdSamCardGenerateKey>(mSamProductType,
439 samCommands.push_back(cmdSamCardGenerateKey);
442 auto samCardRequest = std::make_shared<CardRequestAdapter>(getApduRequests(samCommands),
false);
445 std::shared_ptr<CardResponseApi> samCardResponse;
447 samCardResponse = mSamReader->transmitCardRequest(samCardRequest,
448 ChannelControl::KEEP_OPEN);
449 }
catch (
const UnexpectedStatusWordException& e) {
450 throw IllegalStateException(UNEXPECTED_EXCEPTION,
451 std::make_shared<UnexpectedStatusWordException>(e));
454 std::shared_ptr<ApduResponseApi> cmdSamCardGenerateKeyResponse =
455 samCardResponse->getApduResponses()[cardGenerateKeyCmdIndex];
458 cmdSamCardGenerateKey->setApduResponse(cmdSamCardGenerateKeyResponse).checkStatus();
460 return cmdSamCardGenerateKey->getCipheredData();
464 const std::vector<uint8_t>& poChallenge,
465 const std::vector<uint8_t>& currentPin,
466 const std::vector<uint8_t>& newPin)
468 std::vector<std::shared_ptr<AbstractSamCommand>> samCommands;
469 uint8_t pinCipheringKif;
470 uint8_t pinCipheringKvc;
474 pinCipheringKif = mKif;
475 pinCipheringKvc = mKvc;
477 auto adapter = std::dynamic_pointer_cast<CardSecuritySettingAdapter>(mCardSecuritySettings);
480 if (newPin.empty()) {
483 if (adapter->getPinVerificationCipheringKif() ==
nullptr ||
484 adapter->getPinVerificationCipheringKvc() ==
nullptr) {
485 throw IllegalStateException(
"No KIF or KVC defined for the PIN verification " \
489 pinCipheringKif = *adapter->getPinVerificationCipheringKif();
490 pinCipheringKvc = *adapter->getPinVerificationCipheringKvc();
493 if (adapter->getPinModificationCipheringKif() ==
nullptr ||
494 adapter->getPinModificationCipheringKvc() ==
nullptr) {
495 throw IllegalStateException(
"No KIF or KVC defined for the PIN modification " \
499 pinCipheringKif = *adapter->getPinModificationCipheringKif();
500 pinCipheringKvc = *adapter->getPinModificationCipheringKvc();
504 if (!mIsDiversificationDone) {
509 samCommands.push_back(
510 std::make_shared<CmdSamSelectDiversifier>(mSamProductType,
511 mCalypsoCard->getCalypsoSerialNumberFull()));
512 mIsDiversificationDone =
true;
515 if (mIsDigesterInitialized) {
519 Arrays::addAll(samCommands, getPendingSamCommands(
false));
522 samCommands.push_back(std::make_shared<CmdSamGiveRandom>(mSamProductType, poChallenge));
524 const size_t cardCipherPinCmdIndex = samCommands.size();
526 auto cmdSamCardCipherPin = std::make_shared<CmdSamCardCipherPin>(mSamProductType,
532 samCommands.push_back(cmdSamCardCipherPin);
535 auto samCardRequest = std::make_shared<CardRequestAdapter>(getApduRequests(samCommands),
false);
538 std::shared_ptr<CardResponseApi> samCardResponse;
540 samCardResponse = mSamReader->transmitCardRequest(samCardRequest,
541 ChannelControl::KEEP_OPEN);
542 }
catch (
const UnexpectedStatusWordException& e) {
543 throw IllegalStateException(UNEXPECTED_EXCEPTION,
544 std::make_shared<UnexpectedStatusWordException>(e));
547 std::shared_ptr<ApduResponseApi> cardCipherPinResponse =
548 samCardResponse->getApduResponses()[cardCipherPinCmdIndex];
551 cmdSamCardCipherPin->setApduResponse(cardCipherPinResponse).checkStatus();
553 return cmdSamCardCipherPin->getCipheredData();
556const std::vector<uint8_t> SamCommandProcessor::getSvComplementaryData(
557 const std::shared_ptr<AbstractSamCommand> cmdSamSvPrepare)
559 std::vector<std::shared_ptr<AbstractSamCommand>> samCommands;
561 if (!mIsDiversificationDone) {
566 samCommands.push_back(
567 std::make_shared<CmdSamSelectDiversifier>(mSamProductType,
568 mCalypsoCard->getCalypsoSerialNumberFull()));
569 mIsDiversificationDone =
true;
572 if (mIsDigesterInitialized) {
576 Arrays::addAll(samCommands, getPendingSamCommands(
false));
579 const size_t svPrepareOperationCmdIndex = samCommands.size();
581 samCommands.push_back(cmdSamSvPrepare);
584 auto samCardRequest = std::make_shared<CardRequestAdapter>(getApduRequests(samCommands),
false);
587 std::shared_ptr<CardResponseApi> samCardResponse;
589 samCardResponse = mSamReader->transmitCardRequest(samCardRequest,
590 ChannelControl::KEEP_OPEN);
591 }
catch (
const UnexpectedStatusWordException& e) {
592 throw IllegalStateException(UNEXPECTED_EXCEPTION,
593 std::make_shared<UnexpectedStatusWordException>(e));
596 const std::shared_ptr<ApduResponseApi> svPrepareResponse =
597 samCardResponse->getApduResponses()[svPrepareOperationCmdIndex];
600 cmdSamSvPrepare->setApduResponse(svPrepareResponse).checkStatus();
602 std::vector<uint8_t> prepareOperationData = cmdSamSvPrepare->getApduResponse()->getDataOut();
603 std::vector<uint8_t> operationComplementaryData(mSamSerialNumber.size() + prepareOperationData.size());
605 System::arraycopy(mSamSerialNumber, 0, operationComplementaryData, 0, mSamSerialNumber.size());
606 System::arraycopy(prepareOperationData,
608 operationComplementaryData,
609 mSamSerialNumber.size(),
610 prepareOperationData.size());
612 return operationComplementaryData;
616 const std::shared_ptr<CmdCardSvReload> cmdCardSvReload,
617 const std::vector<uint8_t>& svGetHeader,
618 const std::vector<uint8_t>& svGetData)
621 const auto cmdSamSvPrepareLoad =
622 std::make_shared<CmdSamSvPrepareLoad>(mSamProductType,
625 cmdCardSvReload->getSvReloadData());
627 return getSvComplementaryData(cmdSamSvPrepareLoad);
631 const std::shared_ptr<CmdCardSvDebit> cmdCardSvDebit,
632 const std::vector<uint8_t>& svGetHeader,
633 const std::vector<uint8_t>& svGetData)
636 const auto cmdSamSvPrepareDebit =
637 std::make_shared<CmdSamSvPrepareDebit>(mSamProductType,
640 cmdCardSvDebit->getSvDebitData());
642 return getSvComplementaryData(cmdSamSvPrepareDebit);
646 const std::shared_ptr<CmdCardSvUndebit> cmdCardSvUndebit,
647 const std::vector<uint8_t>& svGetHeader,
648 const std::vector<uint8_t>& svGetData)
651 const auto cmdSamSvPrepareUndebit =
652 std::make_shared<CmdSamSvPrepareUndebit>(mSamProductType,
655 cmdCardSvUndebit->getSvUndebitData());
657 return getSvComplementaryData(cmdSamSvPrepareUndebit);
662 std::vector<std::shared_ptr<AbstractSamCommand>> samCommands;
663 const auto cmdSamSvCheck = std::make_shared<CmdSamSvCheck>(mSamProductType,
664 svOperationResponseData);
665 samCommands.push_back(cmdSamSvCheck);
668 auto samCardRequest = std::dynamic_pointer_cast<CardRequestSpi>(
669 std::make_shared<CardRequestAdapter>(getApduRequests(samCommands),
673 std::shared_ptr<CardResponseApi> samCardResponse;
675 samCardResponse = mSamReader->transmitCardRequest(samCardRequest,
676 ChannelControl::KEEP_OPEN);
677 }
catch (
const UnexpectedStatusWordException& e) {
678 throw IllegalStateException(UNEXPECTED_EXCEPTION,
679 std::make_shared<UnexpectedStatusWordException>(e));
682 const std::shared_ptr<ApduResponseApi> svCheckResponse = samCardResponse->getApduResponses()[0];
685 cmdSamSvCheck->setApduResponse(svCheckResponse).checkStatus();
const std::vector< uint8_t > getEncryptedKey(const std::vector< uint8_t > &poChallenge, const uint8_t cipheringKif, const uint8_t cipheringKvc, const uint8_t sourceKif, const uint8_t sourceKvc)
const std::vector< uint8_t > getSvReloadComplementaryData(const std::shared_ptr< CmdCardSvReload > cmdCardSvReload, const std::vector< uint8_t > &svGetHeader, const std::vector< uint8_t > &svGetData)
const std::shared_ptr< uint8_t > computeKvc(const WriteAccessLevel writeAccessLevel, const std::shared_ptr< uint8_t > kvc) const
const std::vector< uint8_t > getCipheredPinData(const std::vector< uint8_t > &poChallenge, const std::vector< uint8_t > ¤tPin, const std::vector< uint8_t > &newPin)
const std::vector< uint8_t > getTerminalSignature()
const std::vector< uint8_t > getSessionTerminalChallenge()
void pushCardExchangedData(const std::vector< std::shared_ptr< ApduRequestSpi > > &requests, const std::vector< std::shared_ptr< ApduResponseApi > > &responses, const int startIndex)
const std::shared_ptr< uint8_t > computeKif(const WriteAccessLevel writeAccessLevel, const std::shared_ptr< uint8_t > kif, const std::shared_ptr< uint8_t > kvc)
const std::vector< uint8_t > getSvUndebitComplementaryData(const std::shared_ptr< CmdCardSvUndebit > cmdCardSvUndebit, const std::vector< uint8_t > &svGetHeader, const std::vector< uint8_t > &svGetData)
const std::vector< uint8_t > getSvDebitComplementaryData(const std::shared_ptr< CmdCardSvDebit > cmdCardSvDebit, const std::vector< uint8_t > &svGetHeader, const std::vector< uint8_t > &svGetData)
SamCommandProcessor(const std::shared_ptr< CalypsoCard > calypsoCard, const std::shared_ptr< CardSecuritySetting > cardSecuritySetting)
void authenticateCardSignature(const std::vector< uint8_t > &cardSignatureLo)
void initializeDigester(const bool sessionEncryption, const bool verificationMode, const uint8_t kif, const uint8_t kvc, const std::vector< uint8_t > &digestData)
void checkSvStatus(const std::vector< uint8_t > &svOperationResponseData)