14#include "keyple/card/calypso/CommandOpenSecureSession.hpp"
23#include "keyple/card/calypso/CalypsoCardAdapter.hpp"
24#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
25#include "keyple/card/calypso/CardDataAccessException.hpp"
26#include "keyple/card/calypso/CardIllegalParameterException.hpp"
27#include "keyple/card/calypso/CardSecurityContextException.hpp"
28#include "keyple/card/calypso/CardTerminatedException.hpp"
29#include "keyple/card/calypso/CardUnexpectedResponseLengthException.hpp"
30#include "keyple/core/util/ApduUtil.hpp"
31#include "keyple/core/util/ByteArrayUtil.hpp"
32#include "keyple/core/util/HexUtil.hpp"
33#include "keyple/core/util/cpp/Arrays.hpp"
34#include "keyple/core/util/cpp/System.hpp"
35#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
36#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
37#include "keypop/calypso/card/transaction/CryptoIOException.hpp"
38#include "keypop/calypso/card/transaction/UnauthorizedKeyException.hpp"
39#include "keypop/calypso/crypto/asymmetric/AsymmetricCryptoException.hpp"
45using keyple::core::util::ApduUtil;
46using keyple::core::util::ByteArrayUtil;
47using keyple::core::util::HexUtil;
48using keyple::core::util::cpp::Arrays;
49using keyple::core::util::cpp::System;
50using keyple::core::util::cpp::exception::IllegalArgumentException;
51using keyple::core::util::cpp::exception::IllegalStateException;
52using keypop::calypso::card::transaction::CryptoIOException;
53using keypop::calypso::card::transaction::UnauthorizedKeyException;
54using keypop::calypso::crypto::asymmetric::AsymmetricCryptoException;
56const std::string CommandOpenSecureSession::PATTERN_1_BYTE_HEX =
"%02Xh";
58const std::map<int, const std::shared_ptr<Command::StatusProperties>>
59 CommandOpenSecureSession::STATUS_TABLE = [] {
60 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
61 Command::STATUS_TABLE);
65 std::make_shared<StatusProperties>(
66 "Lc value not supported",
67 typeid(CardIllegalParameterException))},
69 std::make_shared<StatusProperties>(
70 "Transaction Counter is 0",
typeid(CardTerminatedException))},
72 std::make_shared<StatusProperties>(
73 "Command forbidden (read requested and current EF is a Binary"
75 typeid(CardDataAccessException))},
77 std::make_shared<StatusProperties>(
78 "Security conditions not fulfilled (PIN code not presented, "
79 "AES key forbidding the compatibility mode, encryption "
81 typeid(CardSecurityContextException))},
83 std::make_shared<StatusProperties>(
84 "Access forbidden (Never access mode, Session already "
86 typeid(CardAccessForbiddenException))},
88 std::make_shared<StatusProperties>(
89 "Command not allowed (read requested and no current EF)",
90 typeid(CardDataAccessException))},
92 std::make_shared<StatusProperties>(
93 "Wrong key index",
typeid(CardIllegalParameterException))},
95 std::make_shared<StatusProperties>(
96 "File not found",
typeid(CardDataAccessException))},
98 std::make_shared<StatusProperties>(
99 "Record not found (record index is above NumRec)",
100 typeid(CardDataAccessException))},
102 std::make_shared<StatusProperties>(
103 "P1 or P2 value not supported (key index incorrect, wrong P2,"
104 " extended mode not supported)",
105 typeid(CardIllegalParameterException))},
107 std::make_shared<StatusProperties>(
108 "Correct execution (ISO7816 T=0)",
109 typeid(CardIllegalParameterException))},
111 std::make_shared<StatusProperties>(
112 "Successful execution, with warning (Pre-Open variant, secure"
113 " session not opened)",
114 typeid(CardIllegalParameterException))}});
118CommandOpenSecureSession::CommandOpenSecureSession(
119 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
121 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
122 WriteAccessLevel writeAccessLevel)
124 CardCommandRef::OPEN_SECURE_SESSION,
128, mWriteAccessLevel(writeAccessLevel)
129, mIsExtendedModeAllowed(true)
130, mIsPreOpenModeOnSelection(true)
131, mIsPreOpenMode(false)
132, mIsReadModeConfigured(false)
135, mExpectedRecordDataLength(0)
139 static_cast<std::uint8_t
>(
static_cast<int>(mWriteAccessLevel) + 1), {});
140 addSubName(
"PRE-OPEN");
143CommandOpenSecureSession::CommandOpenSecureSession(
144 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
146 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
147 std::shared_ptr<SymmetricCryptoSecuritySettingAdapter>
148 symmetricCryptoSecuritySetting,
149 WriteAccessLevel writeAccessLevel,
150 bool isExtendedModeAllowed)
152 CardCommandRef::OPEN_SECURE_SESSION,
156, mWriteAccessLevel(writeAccessLevel)
157, mIsExtendedModeAllowed(isExtendedModeAllowed)
158, mSymmetricCryptoSecuritySetting(symmetricCryptoSecuritySetting)
159, mIsPreOpenModeOnSelection(false)
160, mIsReadModeConfigured(false)
163, mExpectedRecordDataLength(0)
166 std::shared_ptr<CalypsoCardAdapter> card = transactionContext->getCard();
167 if (card ==
nullptr) {
168 throw IllegalStateException(
"Card is required in transaction context");
171 mPreOpenDataOut = card->getPreOpenDataOut();
172 mIsPreOpenMode = !mPreOpenDataOut.empty();
175CommandOpenSecureSession::CommandOpenSecureSession(
176 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
178 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
179 const std::vector<std::uint8_t>& terminalChallenge)
181 CardCommandRef::OPEN_SECURE_SESSION,
185, mWriteAccessLevel(WriteAccessLevel::UNKOWN)
186, mIsExtendedModeAllowed(true)
187, mIsPreOpenModeOnSelection(false)
188, mIsPreOpenMode(false)
189, mIsReadModeConfigured(false)
192, mExpectedRecordDataLength(0)
194 createRev3Pki(terminalChallenge);
198const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
199CommandOpenSecureSession::getStatusTable()
const
205CommandOpenSecureSession::createRev3(
206 std::uint8_t keyIndex,
const std::vector<std::uint8_t>& samChallenge)
208 const std::uint8_t p1
209 =
static_cast<std::uint8_t
>((mRecordNumber * 8) + keyIndex);
212 std::vector<std::uint8_t> dataIn;
214 if (mIsExtendedModeAllowed) {
215 p2 =
static_cast<std::uint8_t
>((mSfi * 8) + 2);
216 dataIn.resize(samChallenge.size() + 1);
217 System::arraycopy(samChallenge, 0, dataIn, 1, samChallenge.size());
219 p2 =
static_cast<std::uint8_t
>((mSfi * 8) + 1);
220 dataIn = samChallenge;
228 std::unique_ptr<DtoAdapters::ApduRequestAdapter>(
229 new DtoAdapters::ApduRequestAdapter(
231 CalypsoCardClass::ISO.getValue(),
232 CardCommandRef::OPEN_SECURE_SESSION.getInstructionByte(),
239 "Key index: " + std::to_string(keyIndex) +
", "
240 +
"SFI: " + HexUtil::toHex(
static_cast<std::uint8_t
>(mSfi)) +
"h, "
241 +
"Rec: " + std::to_string(mRecordNumber));
245CommandOpenSecureSession::createRev24(
246 std::uint8_t keyIndex,
const std::vector<std::uint8_t>& samChallenge)
248 const std::uint8_t p1
249 =
static_cast<std::uint8_t
>(0x80 + (mRecordNumber * 8) + keyIndex);
250 buildLegacyApduRequest(keyIndex, samChallenge, mSfi, mRecordNumber, p1);
254CommandOpenSecureSession::createRev10(
255 std::uint8_t keyIndex,
const std::vector<std::uint8_t>& samChallenge)
257 const std::uint8_t p1
258 =
static_cast<std::uint8_t
>((mRecordNumber * 8) + keyIndex);
259 buildLegacyApduRequest(keyIndex, samChallenge, mSfi, mRecordNumber, p1);
263CommandOpenSecureSession::createRev3Pki(
264 const std::vector<std::uint8_t>& terminalChallenge)
266 const std::uint8_t p1 = 0x00;
267 const std::uint8_t p2 = 0x03;
269 std::vector<std::uint8_t> dataIn(terminalChallenge.size() + 1);
271 terminalChallenge, 0, dataIn, 1, terminalChallenge.size());
278 std::unique_ptr<DtoAdapters::ApduRequestAdapter>(
279 new DtoAdapters::ApduRequestAdapter(
281 CalypsoCardClass::ISO.getValue(),
282 CardCommandRef::OPEN_SECURE_SESSION.getInstructionByte(),
290CommandOpenSecureSession::buildLegacyApduRequest(
291 std::uint8_t keyIndex,
292 const std::vector<std::uint8_t>& samChallenge,
297 const std::uint8_t p2 =
static_cast<std::uint8_t
>(sfi * 8);
304 std::unique_ptr<DtoAdapters::ApduRequestAdapter>(
305 new DtoAdapters::ApduRequestAdapter(
307 CalypsoCardClass::LEGACY.getValue(),
308 CardCommandRef::OPEN_SECURE_SESSION.getInstructionByte(),
315 "Key index: " + std::to_string(keyIndex) +
", "
316 +
"SFI: " + HexUtil::toHex(
static_cast<std::uint8_t
>(sfi)) +
"h, "
317 +
"Rec: " + std::to_string(recordNumber));
321CommandOpenSecureSession::configureReadMode(
322 int sfi,
int recordNumber,
int expectedRecordDataLength)
324 if (getTransactionContext()->isPkiMode()) {
325 std::shared_ptr<DtoAdapters::ApduRequestAdapter> request
327 std::vector<std::uint8_t> apdu = request->getApdu();
330 apdu[2] =
static_cast<std::uint8_t
>(recordNumber * 8);
331 apdu[3] =
static_cast<std::uint8_t
>((sfi * 8) + 3);
333 request->setApdu(apdu);
337 + HexUtil::toHex(
static_cast<std::uint8_t
>(sfi))
338 +
"h, Rec: " + std::to_string(recordNumber));
342 mRecordNumber = recordNumber;
343 mExpectedRecordDataLength = expectedRecordDataLength;
344 mIsReadModeConfigured =
true;
348CommandOpenSecureSession::isReadModeConfigured()
const
350 return mIsReadModeConfigured;
354CommandOpenSecureSession::finalizeRequest()
356 std::vector<std::uint8_t> samChallenge;
359 samChallenge = getTransactionContext()
360 ->getSymmetricCryptoCardTransactionManagerSpi()
361 ->initTerminalSecureSessionContext();
363 }
catch (
const SymmetricCryptoException& e) {
364 throw CryptoException(e.what(), e);
366 }
catch (
const SymmetricCryptoIOException& e) {
367 throw CryptoIOException(e.what(), e);
370 const std::uint8_t keyIndex
371 =
static_cast<std::uint8_t
>(
static_cast<int>(mWriteAccessLevel) + 1);
373 switch (getTransactionContext()->getCard()->getProductType()) {
374 case CalypsoCard::ProductType::PRIME_REVISION_1:
375 createRev10(keyIndex, samChallenge);
377 case CalypsoCard::ProductType::PRIME_REVISION_2:
378 createRev24(keyIndex, samChallenge);
380 case CalypsoCard::ProductType::PRIME_REVISION_3:
381 case CalypsoCard::ProductType::LIGHT:
382 case CalypsoCard::ProductType::BASIC:
383 createRev3(keyIndex, samChallenge);
386 std::stringstream ss;
387 ss <<
"Unsupported ProductType: "
388 << getTransactionContext()->getCard()->getProductType();
389 throw IllegalArgumentException(ss.str());
394CommandOpenSecureSession::isCryptoServiceRequiredToFinalizeRequest()
const
400CommandOpenSecureSession::synchronizeCryptoServiceBeforeCardProcessing()
402 if (!mIsPreOpenMode) {
410 if (!isCryptoServiceSynchronized()) {
412 parseRev3(mPreOpenDataOut);
414 }
catch (
const CardUnexpectedResponseLengthException& e) {
415 throw IllegalStateException(
416 "Unexpected response length in pre-open mode", e);
419 synchronizeCryptoService(mPreOpenDataOut);
426CommandOpenSecureSession::synchronizeCryptoService(
427 const std::vector<std::uint8_t>& dataOut)
429 if (getTransactionContext()->isPkiMode()) {
431 std::shared_ptr<AsymmetricCryptoCardTransactionManagerSpi>
433 = getTransactionContext()
434 ->getAsymmetricCryptoCardTransactionManagerSpi();
435 cryptoManager->initTerminalPkiSession(
436 getTransactionContext()->getCard()->getCardPublicKeySpi());
437 cryptoManager->updateTerminalPkiSession(
438 getApduRequest()->getApdu());
439 cryptoManager->updateTerminalPkiSession(
440 getApduResponse()->getApdu());
442 }
catch (
const AsymmetricCryptoException& e) {
443 throw CryptoException(e.what(), e);
447 std::shared_ptr<std::uint8_t> computedKvc = computeKvc();
448 std::shared_ptr<std::uint8_t> computedKif
449 = computeKif(computedKvc.get());
450 if (!mSymmetricCryptoSecuritySetting->isSessionKeyAuthorized(
451 computedKif, computedKvc)) {
452 throw UnauthorizedKeyException(
453 std::string(
"Unauthorized key. KIF: ")
454 + HexUtil::toHex(*computedKif)
455 +
", KVC: " + std::to_string(*computedKvc));
459 getTransactionContext()
460 ->getSymmetricCryptoCardTransactionManagerSpi()
461 ->initTerminalSessionMac(dataOut, *computedKif, *computedKvc);
463 }
catch (
const SymmetricCryptoException& e) {
464 throw CryptoException(e.what(), e);
466 }
catch (
const SymmetricCryptoIOException& e) {
467 throw CryptoIOException(e.what(), e);
471 confirmCryptoServiceSuccessfullySynchronized();
475CommandOpenSecureSession::parseResponse(
476 std::shared_ptr<ApduResponseApi> apduResponse)
478 Command::setApduResponseAndCheckStatus(apduResponse);
479 std::shared_ptr<CalypsoCardAdapter> card
480 = getTransactionContext()->getCard();
481 if (!mIsPreOpenModeOnSelection) {
483 getTransactionContext()->setSecureSessionOpen(
true);
487 const std::vector<std::uint8_t> dataOut = getApduResponse()->getDataOut();
488 if (getTransactionContext()->isPkiMode()) {
491 switch (card->getProductType()) {
492 case CalypsoCard::ProductType::PRIME_REVISION_1:
495 case CalypsoCard::ProductType::PRIME_REVISION_2:
507 card->setDfRatified(mIsPreviousSessionRatified);
510 card->setTransactionCounter(
511 ByteArrayUtil::extractInt(mChallengeTransactionCounter, 0, 3,
false));
512 if (mRecordData.size() > 0) {
514 static_cast<std::uint8_t
>(mSfi),
515 static_cast<std::uint8_t
>(mRecordNumber),
522 if (mIsPreOpenModeOnSelection && apduResponse->getStatusWord() == 0x6200) {
523 card->setPreOpenWriteAccessLevel(mWriteAccessLevel);
524 card->setPreOpenDataOut(dataOut);
528 if (!isCryptoServiceSynchronized()) {
529 synchronizeCryptoService(dataOut);
535 if (!Arrays::equals(dataOut, mPreOpenDataOut)) {
536 throw CardSecurityContextException(
537 "Session has been pre-opened but 'dataOut' fields do not match",
538 CardCommandRef::OPEN_SECURE_SESSION);
543std::shared_ptr<std::uint8_t>
544CommandOpenSecureSession::computeKvc()
546 if (mKvc !=
nullptr) {
550 return mSymmetricCryptoSecuritySetting->getDefaultKvc(mWriteAccessLevel);
553std::shared_ptr<std::uint8_t>
554CommandOpenSecureSession::computeKif(
const std::uint8_t* kvc)
557 if ((mKif !=
nullptr && *mKif != 0xFF) || (kvc ==
nullptr)) {
562 std::shared_ptr<std::uint8_t> result
563 = mSymmetricCryptoSecuritySetting->getKif(mWriteAccessLevel, *kvc);
564 if (result ==
nullptr) {
566 = mSymmetricCryptoSecuritySetting->getDefaultKif(mWriteAccessLevel);
573CommandOpenSecureSession::parseRev3(
574 const std::vector<std::uint8_t>& apduResponseData)
579 if (mIsExtendedModeAllowed) {
581 mIsPreviousSessionRatified = (apduResponseData[8] & 0x01) == 0x00;
582 bool manageSecureSessionAuthorized
583 = (apduResponseData[8] & 0x02) == 0x02;
584 if (!manageSecureSessionAuthorized) {
585 getTransactionContext()->getCard()->disableExtendedMode();
590 mIsPreviousSessionRatified = (apduResponseData[4] == 0x00);
591 getTransactionContext()->getCard()->disableExtendedMode();
594 mChallengeTransactionCounter = Arrays::copyOfRange(apduResponseData, 0, 3);
595 mKif = std::make_shared<std::uint8_t>(apduResponseData[5 + offset]);
596 mKvc = std::make_shared<std::uint8_t>(apduResponseData[6 + offset]);
597 int dataLength = apduResponseData[7 + offset];
598 if (dataLength !=
static_cast<int>(apduResponseData.size() - 8 - offset)) {
599 throw CardUnexpectedResponseLengthException(
600 std::string(
"APDU response is not the correct length. Command: ")
601 + getCommandRef().getName()
602 +
", Expected: " + std::to_string(mExpectedRecordDataLength)
603 +
", Actual: " + std::to_string(dataLength),
606 mRecordData = Arrays::copyOfRange(
607 apduResponseData, 8 + offset, 8 + offset + dataLength);
611CommandOpenSecureSession::parseRev24(
612 const std::vector<std::uint8_t>& apduResponseData)
614 switch (apduResponseData.size()) {
616 mIsPreviousSessionRatified =
true;
620 checkReceivedDataLength(29);
621 mIsPreviousSessionRatified =
true;
622 mRecordData = Arrays::copyOfRange(apduResponseData, 5, 34);
625 mIsPreviousSessionRatified =
false;
629 checkReceivedDataLength(29);
630 mIsPreviousSessionRatified =
false;
631 mRecordData = Arrays::copyOfRange(apduResponseData, 7, 36);
634 throw CardUnexpectedResponseLengthException(
635 std::string(
"APDU response is not the correct length. Command: ")
636 + getCommandRef().getName() +
", Expected: 5/7/34/36, Actual: "
637 + std::to_string(apduResponseData.size()),
641 mChallengeTransactionCounter = Arrays::copyOfRange(apduResponseData, 1, 4);
643 mKvc = std::unique_ptr<std::uint8_t>(
new std::uint8_t(apduResponseData[0]));
647CommandOpenSecureSession::parseRev10(
648 const std::vector<std::uint8_t>& apduResponseData)
650 switch (apduResponseData.size()) {
652 mIsPreviousSessionRatified =
true;
656 checkReceivedDataLength(29);
657 mIsPreviousSessionRatified =
true;
658 mRecordData = Arrays::copyOfRange(apduResponseData, 4, 33);
661 mIsPreviousSessionRatified =
false;
665 checkReceivedDataLength(29);
666 mIsPreviousSessionRatified =
false;
667 mRecordData = Arrays::copyOfRange(apduResponseData, 6, 35);
670 throw CardUnexpectedResponseLengthException(
671 "APDU response is not the correct length. Command: "
672 + getCommandRef().getName() +
", Expected: 4/6/33/35, Actual: "
673 + std::to_string(apduResponseData.size()),
677 mChallengeTransactionCounter = Arrays::copyOfRange(apduResponseData, 0, 3);
683CommandOpenSecureSession::parsePki(
684 const std::vector<std::uint8_t>& apduResponseData)
686 const int li = apduResponseData[0] & 0xFF;
687 int offset = 1 + li + 8 + 1;
689 mChallengeTransactionCounter
690 = Arrays::copyOfRange(apduResponseData, offset, offset + 3);
693 mIsPreviousSessionRatified = (apduResponseData[offset] & 0x01) == 0x00;
696 const int ld = apduResponseData[offset] & 0xFF;
699 mRecordData = Arrays::copyOfRange(apduResponseData, offset, offset + ld);
703CommandOpenSecureSession::checkReceivedDataLength(
int dataLength)
705 if (dataLength != mExpectedRecordDataLength) {
706 throw CardUnexpectedResponseLengthException(
707 "APDU response is not the correct length. Command: "
708 + getCommandRef().getName()
709 +
", Expected: " + std::to_string(mExpectedRecordDataLength)
710 +
", Actual: " + std::to_string(dataLength),