14#include "keyple/card/calypso/CommandReadRecords.hpp"
22#include "keyple/card/calypso/CalypsoCardAdapter.hpp"
23#include "keyple/card/calypso/CalypsoCardClass.hpp"
24#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
25#include "keyple/card/calypso/CardCommandRef.hpp"
26#include "keyple/card/calypso/CardDataAccessException.hpp"
27#include "keyple/card/calypso/CardIllegalParameterException.hpp"
28#include "keyple/card/calypso/CardSecurityContextException.hpp"
29#include "keyple/core/util/ApduUtil.hpp"
30#include "keyple/core/util/HexUtil.hpp"
31#include "keyple/core/util/cpp/Arrays.hpp"
32#include "keyple/core/util/cpp/System.hpp"
38using keyple::core::util::ApduUtil;
39using keyple::core::util::HexUtil;
40using keyple::core::util::cpp::Arrays;
41using keyple::core::util::cpp::System;
43const std::map<int, const std::shared_ptr<Command::StatusProperties>>
44 CommandReadRecords::STATUS_TABLE = [] {
45 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
46 Command::STATUS_TABLE);
50 std::make_shared<Command::StatusProperties>(
51 "Command forbidden on binary files",
52 typeid(CardDataAccessException))},
54 std::make_shared<Command::StatusProperties>(
55 "Security conditions not fulfilled (PIN code not presented, "
56 "encryption required)",
57 typeid(CardSecurityContextException))},
59 std::make_shared<Command::StatusProperties>(
60 "Access forbidden (Never access mode, stored value log file "
62 "operation was done during the current session)",
63 typeid(CardAccessForbiddenException))},
65 std::make_shared<Command::StatusProperties>(
66 "Command not allowed (no current EF)",
67 typeid(CardDataAccessException))},
69 std::make_shared<Command::StatusProperties>(
70 "File not found",
typeid(CardDataAccessException))},
72 std::make_shared<Command::StatusProperties>(
73 "Record not found (record index is 0, or above NumRec",
74 typeid(CardDataAccessException))},
76 std::make_shared<Command::StatusProperties>(
77 "P2 value not supported",
78 typeid(CardIllegalParameterException))}});
82CommandReadRecords::CommandReadRecords(
83 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
85 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
87 int firstRecordNumber,
89 std::unique_ptr<int> expectedLength,
92 CardCommandRef::READ_RECORDS,
93 std::move(expectedLength),
97, mFirstRecordNumber(firstRecordNumber)
98, mRecordSize(recordSize)
101 transactionContext->getCard() != nullptr
102 && transactionContext->getCard()->getPreOpenWriteAccessLevel()
103 != WriteAccessLevel::UNKOWN)
105 const std::uint8_t cardClass
106 = transactionContext->getCard() !=
nullptr
107 ? transactionContext->getCard()->getCardClass().getValue()
108 : CalypsoCardClass::ISO.getValue();
110 const std::uint8_t p1 =
static_cast<std::uint8_t
>(firstRecordNumber);
112 =
static_cast<std::uint8_t
>((sfi == 0x00) ? 0x05 : ((sfi * 8) + 5));
113 if (readMode == ReadMode::ONE_RECORD) {
121 const int* expectedResponseLength = getExpectedResponseLength();
122 const std::uint8_t le =
static_cast<std::uint8_t
>(
123 expectedResponseLength !=
nullptr ? *expectedResponseLength : 0x00);
127 std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
128 cardClass, getCommandRef().getInstructionByte(), p1, p2, le)));
130 const std::string subName
131 = std::string(
"SFI: ") + HexUtil::toHex(
static_cast<std::uint32_t
>(sfi))
132 +
"h, Rec: " + std::to_string(firstRecordNumber) +
", Read mode: "
133 + (readMode == CommandReadRecords::ReadMode::ONE_RECORD
136 +
", Expected length: "
137 + (expectedResponseLength !=
nullptr
138 ? std::to_string(*expectedResponseLength)
144CommandReadRecords::finalizeRequest()
146 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
150CommandReadRecords::isCryptoServiceRequiredToFinalizeRequest()
const
152 return getCommandContext()->isEncryptionActive();
156CommandReadRecords::synchronizeCryptoServiceBeforeCardProcessing()
158 if (!getCommandContext()->isSecureSessionOpen()) {
162 if (getCommandContext()->isEncryptionActive()) {
166 if (!mIsPreOpenMode) {
171 if (!isCryptoServiceSynchronized()) {
172 const std::vector<std::uint8_t> anticipatedApduResponse(
173 buildAnticipatedResponse());
174 if (anticipatedApduResponse.empty()) {
175 const std::string sfiHex
176 = HexUtil::toHex(
static_cast<std::uint32_t
>(mSfi));
178 std::string(
"Unable to determine anticipated APDU response ")
179 +
"because the record or some records have not been read "
180 +
"beforehand [command=%, sfi=%, record=%]\n",
188 mAnticipatedDataOut = Arrays::copyOf(
189 anticipatedApduResponse, anticipatedApduResponse.size() - 2);
191 updateTerminalSessionIfNeeded(anticipatedApduResponse);
198CommandReadRecords::parseResponse(std::shared_ptr<ApduResponseApi> apduResponse)
200 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
201 if (!setApduResponseAndCheckStatusInBestEffortMode(apduResponse)) {
205 const std::vector<std::uint8_t> dataOut = apduResponse->getDataOut();
206 if (mReadMode == CommandReadRecords::ReadMode::ONE_RECORD) {
207 getTransactionContext()->getCard()->setContent(
208 static_cast<std::uint8_t
>(mSfi),
209 static_cast<std::uint8_t
>(mFirstRecordNumber),
212 int apduLen =
static_cast<int>(dataOut.size());
214 while (apduLen > 0) {
215 const std::uint8_t recordNb = dataOut[index++];
216 const std::uint8_t len = dataOut[index++];
217 getTransactionContext()->getCard()->setContent(
218 static_cast<std::uint8_t
>(mSfi),
220 Arrays::copyOfRange(dataOut, index, index + len));
222 apduLen -= (2 + len);
225 if (!isCryptoServiceSynchronized()) {
226 updateTerminalSessionIfNeeded();
228 getCommandContext()->isSecureSessionOpen() && mIsPreOpenMode
229 && !Arrays::equals(dataOut, mAnticipatedDataOut)) {
230 throw CardSecurityContextException(
231 "Data out does not match the anticipated data out",
232 CardCommandRef::READ_RECORDS);
236const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
237CommandReadRecords::getStatusTable()
const
242std::vector<std::uint8_t>
243CommandReadRecords::buildAnticipatedResponse()
245 std::shared_ptr<ElementaryFile> ef
246 = getTransactionContext()->getCard()->getFileBySfi(
247 static_cast<std::uint8_t
>(mSfi));
252 return mReadMode == CommandReadRecords::ReadMode::ONE_RECORD
253 ? buildAnticipatedResponseForOneRecordMode(ef)
254 : buildAnticipatedResponseForMultipleRecordsMode(ef);
257std::vector<std::uint8_t>
258CommandReadRecords::buildAnticipatedResponseForOneRecordMode(
259 const std::shared_ptr<ElementaryFile>& ef)
261 const std::vector<std::uint8_t> content = ef->getData()->getContent(
262 static_cast<std::uint8_t
>(mFirstRecordNumber));
264 const int expectedResponseLength = *getExpectedResponseLength();
265 if (content.size() > 0
266 &&
static_cast<int>(content.size()) >= expectedResponseLength) {
267 const int length = expectedResponseLength != 0
268 ? expectedResponseLength
269 :
static_cast<int>(content.size());
270 std::vector<std::uint8_t> apdu(length + 2);
272 System::arraycopy(content, 0, apdu, 0, length);
282std::vector<std::uint8_t>
283CommandReadRecords::buildAnticipatedResponseForMultipleRecordsMode(
284 const std::shared_ptr<ElementaryFile>& ef)
286 std::vector<std::uint8_t> apdu(*getExpectedResponseLength() + 2);
287 const int nbRecords = *getExpectedResponseLength() / (mRecordSize + 2);
288 const int lastRecordNumber = mFirstRecordNumber + nbRecords - 1;
291 for (
int i = mFirstRecordNumber; i <= lastRecordNumber; i++) {
292 const std::vector<std::uint8_t> content
293 = ef->getData()->getContent(
static_cast<std::uint8_t
>(i));
294 if (
static_cast<int>(content.size()) >= mRecordSize) {
296 apdu[index++] =
static_cast<std::uint8_t
>(i);
298 apdu[index++] =
static_cast<std::uint8_t
>(mRecordSize);
300 System::arraycopy(content, 0, apdu, index, mRecordSize);
301 index += mRecordSize;