Keyple Card Calypso C++ Library 2.2.5.6
Reference Terminal Reader API for C++
CalypsoCardSelectionAdapter.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/* Calypsonet Terminal Card */
16#include "ParseException.h"
17
18/* Calypsonet Terminal Calypso */
19#include "InconsistentDataException.h"
20#include "SelectFileException.h"
21#include "UnexpectedCommandStatusException.h"
22
23/* Keyple Card Calypso */
24#include "CalypsoCardAdapter.h"
25#include "CalypsoCardClass.h"
26#include "CalypsoCardConstant.h"
30#include "CmdCardGetDataFci.h"
31#include "CmdCardGetDataFcp.h"
34#include "CmdCardReadRecords.h"
35#include "CmdCardSelectFile.h"
36#include "UnsupportedOperationException.h"
37
38/* Keyple Card Generic */
39#include "CardRequestAdapter.h"
40
41/* Keyple Core Util */
42#include "ByteArrayUtil.h"
43#include "HexUtil.h"
44#include "IllegalArgumentException.h"
45#include "KeypleAssert.h"
46#include "Pattern.h"
47#include "PatternSyntaxException.h"
48
49namespace keyple {
50namespace card {
51namespace calypso {
52
53using namespace calypsonet::terminal::card::spi;
54using namespace calypsonet::terminal::calypso::transaction;
55using namespace keyple::core::util;
56using namespace keyple::core::util::cpp;
57using namespace keyple::core::util::cpp::exception;
58
59const int CalypsoCardSelectionAdapter::AID_MIN_LENGTH = 5;
60const int CalypsoCardSelectionAdapter::AID_MAX_LENGTH = 16;
61const int CalypsoCardSelectionAdapter::SW_CARD_INVALIDATED = 0x6283;
62const std::string CalypsoCardSelectionAdapter::MSG_CARD_COMMAND_ERROR =
63 "A card command error occurred ";
64
66: mCardSelector(std::make_shared<CardSelectorAdapter>()) {}
67
69 const std::string& cardProtocol)
70{
71 Assert::getInstance().notEmpty(cardProtocol, "cardProtocol");
72
73 mCardSelector->filterByCardProtocol(cardProtocol);
74
75 return *this;
76}
77
79 const std::string& powerOnDataRegex)
80{
81 Assert::getInstance().notEmpty(powerOnDataRegex, "powerOnDataRegex");
82
83 try {
84 Pattern::compile(powerOnDataRegex);
85 } catch (const PatternSyntaxException& exception) {
86 (void)exception;
87 throw IllegalArgumentException("Invalid regular expression: '" +
88 powerOnDataRegex +
89 "'.");
90 }
91
92 mCardSelector->filterByPowerOnData(powerOnDataRegex);
93
94 return *this;
95}
96
97CalypsoCardSelection& CalypsoCardSelectionAdapter::filterByDfName(const std::vector<uint8_t>& aid)
98{
99 Assert::getInstance().notEmpty(aid, "aid")
100 .isInRange(aid.size(), AID_MIN_LENGTH, AID_MAX_LENGTH, "aid");
101
102 mCardSelector->filterByDfName(aid);
103
104 return *this;
105}
106
107CalypsoCardSelection& CalypsoCardSelectionAdapter::filterByDfName(const std::string& aid)
108{
109 Assert::getInstance().isHexString(aid, "aid format");
110
111 filterByDfName(HexUtil::toByteArray(aid));
112
113 return *this;
114}
115
117 const FileOccurrence fileOccurrence)
118{
119 switch (fileOccurrence) {
120 case FileOccurrence::FIRST:
121 mCardSelector->setFileOccurrence(CardSelectorSpi::FileOccurrence::FIRST);
122 break;
123 case FileOccurrence::LAST:
124 mCardSelector->setFileOccurrence(CardSelectorSpi::FileOccurrence::LAST);
125 break;
126 case FileOccurrence::NEXT:
127 mCardSelector->setFileOccurrence(CardSelectorSpi::FileOccurrence::NEXT);
128 break;
129 case FileOccurrence::PREVIOUS:
130 mCardSelector->setFileOccurrence(CardSelectorSpi::FileOccurrence::PREVIOUS);
131 break;
132 }
133
134 return *this;
135}
136
138 const FileControlInformation fileControlInformation)
139{
140 if (fileControlInformation == FileControlInformation::FCI) {
141 mCardSelector->setFileControlInformation(CardSelectorSpi::FileControlInformation::FCI);
142 } else if (fileControlInformation == FileControlInformation::NO_RESPONSE) {
143 mCardSelector->setFileControlInformation(
144 CardSelectorSpi::FileControlInformation::NO_RESPONSE);
145 }
146
147 return *this;
148}
149
150CalypsoCardSelection& CalypsoCardSelectionAdapter::addSuccessfulStatusWord(const int statusWord)
151{
152 Assert::getInstance().isInRange(statusWord, 0, 0xFFFF, "statusWord");
153
154 mCardSelector->addSuccessfulStatusWord(statusWord);
155
156 return *this;
157}
158
160{
161 mCardSelector->addSuccessfulStatusWord(SW_CARD_INVALIDATED);
162
163 return *this;
164}
165
167 const uint8_t sfi, const uint8_t recordNumber)
168{
169 return prepareReadRecord(sfi, recordNumber);
170}
171
172CalypsoCardSelection& CalypsoCardSelectionAdapter::prepareReadRecord(const uint8_t sfi,
173 const uint8_t recordNumber)
174{
175 Assert::getInstance().isInRange(sfi,
178 "sfi")
179 .isInRange(recordNumber,
182 "recordNumber");
183
184 mCommands.push_back(
185 std::make_shared<CmdCardReadRecords>(CalypsoCardClass::ISO,
186 sfi,
187 recordNumber,
189 static_cast<uint8_t>(0)));
190
191 return *this;
192}
193
194CalypsoCardSelection& CalypsoCardSelectionAdapter::prepareGetData(const GetDataTag tag)
195{
196 /* Create the command and add it to the list of commands */
197 switch (tag) {
198 case GetDataTag::FCI_FOR_CURRENT_DF:
199 mCommands.push_back(std::make_shared<CmdCardGetDataFci>(CalypsoCardClass::ISO));
200 break;
201 case GetDataTag::FCP_FOR_CURRENT_FILE:
202 mCommands.push_back(std::make_shared<CmdCardGetDataFcp>(CalypsoCardClass::ISO));
203 break;
204 case GetDataTag::EF_LIST:
205 mCommands.push_back(std::make_shared<CmdCardGetDataEfList>(CalypsoCardClass::ISO));
206 break;
207 case GetDataTag::TRACEABILITY_INFORMATION:
208 mCommands.push_back(
209 std::make_shared<CmdCardGetDataTraceabilityInformation>(CalypsoCardClass::ISO));
210 break;
211 default:
212 throw UnsupportedOperationException("Unsupported Get Data tag: "); // FIXME: + tag.name());
213 }
214
215 return *this;
216}
217
219 const std::vector<uint8_t>& lid)
220{
221 Assert::getInstance().isEqual(lid.size(), 2, "lid length");
222
223 return prepareSelectFile(static_cast<uint16_t>(ByteArrayUtil::extractInt(lid, 0, 2, false)));
224}
225
226CalypsoCardSelection& CalypsoCardSelectionAdapter::prepareSelectFile(const uint16_t lid)
227{
228 mCommands.push_back(
229 std::make_shared<CmdCardSelectFile>(CalypsoCardClass::ISO,
230 CalypsoCard::ProductType::PRIME_REVISION_3, lid));
231
232 return *this;
233}
234
236 const SelectFileControl selectControl)
237{
238 mCommands.push_back(std::make_shared<CmdCardSelectFile>(CalypsoCardClass::ISO, selectControl));
239
240 return *this;
241}
242
243const std::shared_ptr<CardSelectionRequestSpi>
245{
246 std::vector<std::shared_ptr<ApduRequestSpi>> cardSelectionApduRequests;
247
248 if (!mCommands.empty()) {
249
250 for (const auto& command : mCommands) {
251
252 cardSelectionApduRequests.push_back(command->getApduRequest());
253 }
254
255 return std::make_shared<CardSelectionRequestAdapter>(
256 mCardSelector,
257 std::make_shared<CardRequestAdapter>(cardSelectionApduRequests, false));
258
259 } else {
260
261 return std::make_shared<CardSelectionRequestAdapter>(mCardSelector, nullptr);
262 }
263}
264
265const std::shared_ptr<SmartCardSpi> CalypsoCardSelectionAdapter::parse(
266 const std::shared_ptr<CardSelectionResponseApi> cardSelectionResponse)
267{
268 const std::shared_ptr<CardResponseApi> cardResponse = cardSelectionResponse->getCardResponse();
269
270 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses =
271 cardResponse != nullptr ?
272 cardResponse->getApduResponses() : std::vector<std::shared_ptr<ApduResponseApi>>();
273
274 if (mCommands.size() != apduResponses.size()) {
275
276 throw ParseException("Mismatch in the number of requests/responses.");
277 }
278
279 std::shared_ptr<CalypsoCardAdapter> calypsoCard;
280
281 try {
282
283 calypsoCard = std::make_shared<CalypsoCardAdapter>();
284 calypsoCard->initialize(cardSelectionResponse);
285
286 if (!mCommands.empty()) {
287
288 parseApduResponses(calypsoCard, mCommands, apduResponses);
289 }
290
291 } catch (const Exception& e) {
292
293 throw ParseException("Invalid card response: " + e.getMessage(),
294 std::make_shared<Exception>(e));
295 }
296
297 if (calypsoCard->getProductType() == CalypsoCard::ProductType::UNKNOWN &&
298 cardSelectionResponse->getSelectApplicationResponse() == nullptr &&
299 cardSelectionResponse->getPowerOnData() == "") {
300
301 throw ParseException("Unable to create a CalypsoCard: no power-on data and no FCI " \
302 "provided.");
303 }
304
305 return calypsoCard;
306}
307
308void CalypsoCardSelectionAdapter::parseApduResponses(
309 const std::shared_ptr<CalypsoCardAdapter> calypsoCard,
310 const std::vector<std::shared_ptr<AbstractApduCommand>>& commands,
311 const std::vector<std::shared_ptr<ApduResponseApi>>& apduResponses)
312{
313 /*
314 * If there are more responses than requests, then we are unable to fill the card image. In this
315 * case we stop processing immediately because it may be a case of fraud, and we throw a
316 * desynchronized exception.
317 */
318 if (apduResponses.size() > commands.size()) {
319
320 throw InconsistentDataException("The number of commands/responses does not match: nb " \
321 "commands = " +
322 std::to_string(commands.size()) +
323 ", nb responses = " +
324 std::to_string(apduResponses.size()));
325 }
326
327 /*
328 * We go through all the responses (and not the requests) because there may be fewer in the
329 * case of an error that occurred in strict mode. In this case the last response will raise an
330 * exception.
331 */
332 for (int i = 0; i < static_cast<int>(apduResponses.size()); i++) {
333
334 auto command = std::dynamic_pointer_cast<AbstractCardCommand>(commands[i]);
335
336 try {
337
338 command->parseApduResponse(apduResponses[i], calypsoCard);
339
340 } catch (const CardCommandException& e) {
341
342 const CalypsoCardCommand& commandRef =
343 std::dynamic_pointer_cast<AbstractCardCommand>(command)->getCommandRef();
344
345 try {
346
347 (void)dynamic_cast<const CardDataAccessException&>(e);
348
349 if (commandRef == CalypsoCardCommand::READ_RECORDS) {
350
351 /*
352 * Best effort mode, do not throw exception for "file not found" and "record not
353 * found errors.
354 */
355 if (command->getApduResponse()->getStatusWord() != 0x6A82 &&
356 command->getApduResponse()->getStatusWord() != 0x6A83) {
357
358 throw e;
359 }
360
361 } else if (commandRef == CalypsoCardCommand::SELECT_FILE) {
362
363 throw SelectFileException("File not found",
364 std::make_shared<CardCommandException>(e));
365
366 }else {
367
368 throw UnexpectedCommandStatusException(
369 std::string(MSG_CARD_COMMAND_ERROR) +
370 "while processing responses to card commands: " +
371 e.getCommand().getName(),
372 std::make_shared<CardCommandException>(e));
373 }
374
375 } catch (const std::bad_cast& ex) {
376
377 throw UnexpectedCommandStatusException(
378 std::string(MSG_CARD_COMMAND_ERROR) +
379 "while processing responses to card commands: " +
380 e.getCommand().getName(),
381 std::make_shared<CardCommandException>(e));
382 }
383 }
384 }
385
386 /*
387 * Finally, if no error has occurred and there are fewer responses than requests, then we
388 * throw a desynchronized exception.
389 */
390 if (apduResponses.size() < commands.size()) {
391 throw InconsistentDataException("The number of commands/responses does not match: nb " \
392 "commands = " +
393 std::to_string(commands.size()) +
394 ", nb responses = " +
395 std::to_string(apduResponses.size()));
396 }
397}
398
399}
400}
401}
static const CalypsoCardClass ISO
static const CalypsoCardCommand SELECT_FILE
static const CalypsoCardCommand READ_RECORDS
CalypsoCardSelection & filterByPowerOnData(const std::string &powerOnDataRegex) override
CalypsoCardSelection & addSuccessfulStatusWord(const int statusWord) override
CalypsoCardSelection & prepareGetData(const GetDataTag tag) override
CalypsoCardSelection & prepareReadRecordFile(const uint8_t sfi, const uint8_t recordNumber) override
CalypsoCardSelection & setFileOccurrence(const FileOccurrence fileOccurrence) override
CalypsoCardSelection & setFileControlInformation(const FileControlInformation fileControlInformation) override
const std::shared_ptr< SmartCardSpi > parse(const std::shared_ptr< CardSelectionResponseApi > cardSelectionResponse) override
const std::shared_ptr< CardSelectionRequestSpi > getCardSelectionRequest() override
CalypsoCardSelection & prepareReadRecord(const uint8_t sfi, const uint8_t recordNumber) override
CalypsoCardSelection & filterByDfName(const std::vector< uint8_t > &aid) override
CalypsoCardSelection & filterByCardProtocol(const std::string &cardProtocol) override
CalypsoCardSelection & prepareSelectFile(const std::vector< uint8_t > &lid) override
CardSelectorSpi::FileOccurrence FileOccurrence
CardSelectorSpi::FileControlInformation FileControlInformation