Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandSelectFile.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/card/calypso/CommandSelectFile.hpp"
15
16#include <map>
17#include <memory>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "keyple/card/calypso/CalypsoCardAdapter.hpp"
23#include "keyple/card/calypso/CalypsoCardConstant.hpp"
24#include "keyple/card/calypso/CardCommandRef.hpp"
25#include "keyple/card/calypso/CardDataAccessException.hpp"
26#include "keyple/card/calypso/CardIllegalParameterException.hpp"
27#include "keyple/card/calypso/DirectoryHeaderAdapter.hpp"
28#include "keyple/core/util/ApduUtil.hpp"
29#include "keyple/core/util/BerTlvUtil.hpp"
30#include "keyple/core/util/ByteArrayUtil.hpp"
31#include "keyple/core/util/HexUtil.hpp"
32#include "keyple/core/util/KeypleAssert.hpp"
33#include "keyple/core/util/cpp/Arrays.hpp"
34#include "keyple/core/util/cpp/System.hpp"
35#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
36#include "keypop/calypso/card/transaction/SelectFileException.hpp"
37
38namespace keyple {
39namespace card {
40namespace calypso {
41
42using keyple::core::util::ApduUtil;
43using keyple::core::util::Assert;
44using keyple::core::util::BerTlvUtil;
45using keyple::core::util::ByteArrayUtil;
46using keyple::core::util::HexUtil;
47using keyple::core::util::cpp::Arrays;
48using keyple::core::util::cpp::System;
49using keyple::core::util::cpp::exception::IllegalStateException;
50using keypop::calypso::card::transaction::SelectFileException;
51
52const int CommandSelectFile::TAG_PROPRIETARY_INFORMATION = 0x85;
53const CardCommandRef CommandSelectFile::mCommandRef
54 = CardCommandRef::SELECT_FILE;
55
56const std::map<int, const std::shared_ptr<Command::StatusProperties>>
57 CommandSelectFile::STATUS_TABLE = [] {
58 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
59 Command::STATUS_TABLE);
60
61 m.insert(
62 {{0x6700,
63 std::make_shared<StatusProperties>(
64 "Lc value not supported",
65 typeid(CardIllegalParameterException))},
66 {0x6A82,
67 std::make_shared<StatusProperties>(
68 "File not found", typeid(CardDataAccessException))},
69 {0x6119,
70 std::make_shared<StatusProperties>(
71 "Correct execution (ISO7816 T=0)", typeid(nullptr))}});
72 return m;
73 }();
74
75CommandSelectFile::CommandSelectFile(
76 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
77 transactionContext,
78 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
79 SelectFileControl selectFileControl)
80: Command(
81 CardCommandRef::SELECT_FILE,
82 std::unique_ptr<int>(new int(25)),
83 transactionContext,
84 commandContext)
85{
86 const std::uint8_t cardClass
87 = transactionContext->getCard() != nullptr
88 ? transactionContext->getCard()->getCardClass().getValue()
89 : CalypsoCardClass::ISO.getValue();
90
91 std::uint8_t p1;
92 std::uint8_t p2;
93 std::vector<std::uint8_t> selectData = {0x00, 0x00};
94
95 switch (selectFileControl) {
96 case SelectFileControl::FIRST_EF:
97 p1 = 0x02;
98 p2 = 0x00;
99 break;
100
101 case SelectFileControl::NEXT_EF:
102 p1 = 0x02;
103 p2 = 0x02;
104 break;
105
106 case SelectFileControl::CURRENT_DF:
107 /* CL-KEY-KIFSF.1 */
108 p1 = 0x09;
109 p2 = 0x00;
110 break;
111
112 default:
113 throw IllegalStateException(
114 "Unsupported SelectFileControl: "); // FIXME +
115 // selectFileControl.name()
116 }
117
118 /* APDU Case 4 */
119 setApduRequest(
120 std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
121 cardClass,
122 mCommandRef.getInstructionByte(),
123 p1,
124 p2,
125 selectData,
126 0x00)));
127
128 addSubName("Select file control: x"); // FIXME: selectFileControl
129}
130
131CommandSelectFile::CommandSelectFile(
132 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
133 transactionContext,
134 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
135 std::uint16_t lid)
136: Command(
137 CardCommandRef::SELECT_FILE,
138 std::unique_ptr<int>(new int(25)),
139 transactionContext,
140 commandContext)
141{
142 CalypsoCardClass calypsoCardClass = CalypsoCardClass::UNKNOWN;
143 CalypsoCard::ProductType productType;
144 bool forceRevision1Settings;
145
146 if (transactionContext->getCard() != nullptr) {
147 std::shared_ptr<CalypsoCardAdapter> calypsoCard
148 = transactionContext->getCard();
149 calypsoCardClass = calypsoCard->getCardClass();
150 productType = calypsoCard->getProductType();
151 forceRevision1Settings = calypsoCard->isLegacyCase1();
152 } else {
153 calypsoCardClass = CalypsoCardClass::ISO;
154 productType = CalypsoCard::ProductType::PRIME_REVISION_3;
155 forceRevision1Settings = false;
156 }
157
158 /*
159 * Handle the REV1 case.
160 * CL-KEY-KIFSF.1.
161 * If legacy and rev2 then 02h else if legacy then 08h else 09h.
162 */
163 std::uint8_t p1;
164
165 if (calypsoCardClass == CalypsoCardClass::LEGACY) {
166 if (productType == CalypsoCard::ProductType::PRIME_REVISION_1
167 || forceRevision1Settings) {
168 p1 = 0x08;
169 } else {
170 p1 = 0x02;
171 }
172
173 } else {
174 p1 = 0x09;
175 }
176
177 const std::vector<std::uint8_t> dataIn
178 = ByteArrayUtil::extractBytes(lid, 2);
179
180 /* APDU Case 4 */
181 setApduRequest(
182 std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
183 calypsoCardClass.getValue(),
184 mCommandRef.getInstructionByte(),
185 p1,
186 0x00,
187 dataIn,
188 0x00)));
189
190 addSubName(std::string("LID: ") + HexUtil::toHex(dataIn) + "h");
191}
192
193void
194CommandSelectFile::finalizeRequest()
195{
196 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
197}
198
199bool
200CommandSelectFile::isCryptoServiceRequiredToFinalizeRequest() const
201{
202 return getCommandContext()->isEncryptionActive();
203}
204
205bool
206CommandSelectFile::synchronizeCryptoServiceBeforeCardProcessing()
207{
208 return !getCommandContext()->isSecureSessionOpen();
209}
210
211void
212CommandSelectFile::parseResponse(std::shared_ptr<ApduResponseApi> apduResponse)
213{
214 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
215
216 try {
217 Command::setApduResponseAndCheckStatus(apduResponse);
218
219 } catch (const CardDataAccessException& e) {
220 throw SelectFileException("File not found", e);
221 }
222
223 parseProprietaryInformation(
224 apduResponse->getDataOut(), getTransactionContext()->getCard());
225 updateTerminalSessionIfNeeded();
226}
227
228const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
229CommandSelectFile::getStatusTable() const
230{
231 return STATUS_TABLE;
232}
233
234void
235CommandSelectFile::parseProprietaryInformation(
236 const std::vector<std::uint8_t>& dataOut,
237 std::shared_ptr<CalypsoCardAdapter> calypsoCard)
238{
239 const std::vector<std::uint8_t> proprietaryInformation
240 = getProprietaryInformation(dataOut);
241
242 const std::uint8_t sfi
243 = proprietaryInformation[CalypsoCardConstant::SEL_SFI_OFFSET];
244 const std::uint8_t fileType
245 = proprietaryInformation[CalypsoCardConstant::SEL_TYPE_OFFSET];
246
247 switch (fileType) {
248 case CalypsoCardConstant::FILE_TYPE_MF:
249 case CalypsoCardConstant::FILE_TYPE_DF: {
250 std::unique_ptr<DirectoryHeader> directoryHeader
251 = createDirectoryHeader(proprietaryInformation, calypsoCard);
252 calypsoCard->setDirectoryHeader(std::move(directoryHeader));
253 } break;
254 case CalypsoCardConstant::FILE_TYPE_EF: {
255 std::shared_ptr<FileHeader> fileHeader
256 = createFileHeader(proprietaryInformation, calypsoCard);
257 calypsoCard->setFileHeader(
258 sfi, std::dynamic_pointer_cast<FileHeaderAdapter>(fileHeader));
259 } break;
260 default:
261 throw IllegalStateException(
262 "Unsupported file type: " + HexUtil::toHex(fileType));
263 }
264}
265
266std::vector<std::uint8_t>
267CommandSelectFile::getProprietaryInformation(
268 const std::vector<std::uint8_t>& dataOut)
269{
270 std::vector<std::uint8_t> proprietaryInformation;
271 const std::map<const int, const std::vector<std::uint8_t>> tags
272 = BerTlvUtil::parseSimple(dataOut, true);
273
274 const auto it = tags.find(TAG_PROPRIETARY_INFORMATION);
275 if (it == tags.end()) {
276 throw IllegalStateException("Proprietary information tag not found");
277 }
278
279 proprietaryInformation = it->second;
280
281 Assert::getInstance().isEqual(
282 proprietaryInformation.size(), 23, "proprietaryInformation");
283
284 return proprietaryInformation;
285}
286
287std::unique_ptr<DirectoryHeader>
288CommandSelectFile::createDirectoryHeader(
289 const std::vector<std::uint8_t>& proprietaryInformation,
290 const std::shared_ptr<CalypsoCardAdapter>& calypsoCard)
291{
292 std::vector<std::uint8_t> accessConditions(
293 CalypsoCardConstant::SEL_AC_LENGTH);
294 System::arraycopy(
295 proprietaryInformation,
296 CalypsoCardConstant::SEL_AC_OFFSET,
297 accessConditions,
298 0,
299 CalypsoCardConstant::SEL_AC_LENGTH);
300
301 std::vector<std::uint8_t> keyIndexes(CalypsoCardConstant::SEL_NKEY_LENGTH);
302 System::arraycopy(
303 proprietaryInformation,
304 CalypsoCardConstant::SEL_NKEY_OFFSET,
305 keyIndexes,
306 0,
307 CalypsoCardConstant::SEL_NKEY_LENGTH);
308
309 const std::uint8_t dfStatus
310 = proprietaryInformation[CalypsoCardConstant::SEL_DF_STATUS_OFFSET];
311
312 int lidOffset = calypsoCard->getProductType()
313 == CalypsoCard::ProductType::PRIME_REVISION_2
314 ? CalypsoCardConstant::SEL_LID_OFFSET_REV2
315 : CalypsoCardConstant::SEL_LID_OFFSET;
316
317 std::uint16_t lid
318 = ByteArrayUtil::extractShort(proprietaryInformation, lidOffset);
319
320 return DirectoryHeaderAdapter::builder()
321 ->lid(lid)
322 .accessConditions(accessConditions)
323 .keyIndexes(keyIndexes)
324 .dfStatus(dfStatus)
325 .kvc(
326 WriteAccessLevel::PERSONALIZATION,
327 proprietaryInformation[CalypsoCardConstant::SEL_KVCS_OFFSET])
328 .kvc(
329 WriteAccessLevel::LOAD,
330 proprietaryInformation[CalypsoCardConstant::SEL_KVCS_OFFSET + 1])
331 .kvc(
332 WriteAccessLevel::DEBIT,
333 proprietaryInformation[CalypsoCardConstant::SEL_KVCS_OFFSET + 2])
334 .kif(
335 WriteAccessLevel::PERSONALIZATION,
336 proprietaryInformation[CalypsoCardConstant::SEL_KIFS_OFFSET])
337 .kif(
338 WriteAccessLevel::LOAD,
339 proprietaryInformation[CalypsoCardConstant::SEL_KIFS_OFFSET + 1])
340 .kif(
341 WriteAccessLevel::DEBIT,
342 proprietaryInformation[CalypsoCardConstant::SEL_KIFS_OFFSET + 2])
343 .build();
344}
345
346std::shared_ptr<FileHeaderAdapter>
347CommandSelectFile::createFileHeader(
348 const std::vector<std::uint8_t>& proprietaryInformation,
349 const std::shared_ptr<CalypsoCardAdapter>& calypsoCard)
350{
351 const ElementaryFile::Type fileType = getEfTypeFromCardValue(
352 proprietaryInformation[CalypsoCardConstant::SEL_EF_TYPE_OFFSET]);
353
354 int recordSize;
355 int recordsNumber;
356
357 if (fileType == ElementaryFile::Type::BINARY) {
358 recordSize = ByteArrayUtil::extractInt(
359 proprietaryInformation,
360 CalypsoCardConstant::SEL_REC_SIZE_OFFSET,
361 2,
362 false);
363 recordsNumber = 1;
364
365 } else {
366 recordSize
367 = proprietaryInformation[CalypsoCardConstant::SEL_REC_SIZE_OFFSET];
368 recordsNumber
369 = proprietaryInformation[CalypsoCardConstant::SEL_NUM_REC_OFFSET];
370 }
371
372 std::vector<std::uint8_t> accessConditions(
373 CalypsoCardConstant::SEL_AC_LENGTH);
374 System::arraycopy(
375 proprietaryInformation,
376 CalypsoCardConstant::SEL_AC_OFFSET,
377 accessConditions,
378 0,
379 CalypsoCardConstant::SEL_AC_LENGTH);
380
381 std::vector<std::uint8_t> keyIndexes(CalypsoCardConstant::SEL_NKEY_LENGTH);
382 System::arraycopy(
383 proprietaryInformation,
384 CalypsoCardConstant::SEL_NKEY_OFFSET,
385 keyIndexes,
386 0,
387 CalypsoCardConstant::SEL_NKEY_LENGTH);
388
389 const std::uint8_t dfStatus
390 = proprietaryInformation[CalypsoCardConstant::SEL_DF_STATUS_OFFSET];
391
392 std::uint16_t sharedReference = ByteArrayUtil::extractShort(
393 proprietaryInformation, CalypsoCardConstant::SEL_DATA_REF_OFFSET);
394
395 int lidOffset = calypsoCard->getProductType()
396 == CalypsoCard::ProductType::PRIME_REVISION_2
397 ? CalypsoCardConstant::SEL_LID_OFFSET_REV2
398 : CalypsoCardConstant::SEL_LID_OFFSET;
399
400 std::uint16_t lid
401 = ByteArrayUtil::extractShort(proprietaryInformation, lidOffset);
402
403 return FileHeaderAdapter::builder()
404 ->lid(lid)
405 .recordsNumber(recordsNumber)
406 .recordSize(recordSize)
407 .type(fileType)
408 .accessConditions(
409 Arrays::copyOf(accessConditions, accessConditions.size()))
410 .keyIndexes(Arrays::copyOf(keyIndexes, keyIndexes.size()))
411 .dfStatus(dfStatus)
412 .sharedReference(sharedReference)
413 .build();
414}
415
416ElementaryFile::Type
417CommandSelectFile::getEfTypeFromCardValue(std::uint8_t efType)
418{
419 ElementaryFile::Type fileType;
420
421 switch (efType) {
422 case CalypsoCardConstant::EF_TYPE_BINARY:
423 fileType = ElementaryFile::Type::BINARY;
424 break;
425 case CalypsoCardConstant::EF_TYPE_LINEAR:
426 fileType = ElementaryFile::Type::LINEAR;
427 break;
428 case CalypsoCardConstant::EF_TYPE_CYCLIC:
429 fileType = ElementaryFile::Type::CYCLIC;
430 break;
431 case CalypsoCardConstant::EF_TYPE_SIMULATED_COUNTERS:
432 fileType = ElementaryFile::Type::SIMULATED_COUNTERS;
433 break;
434 case CalypsoCardConstant::EF_TYPE_COUNTERS:
435 fileType = ElementaryFile::Type::COUNTERS;
436 break;
437 default:
438 throw IllegalStateException(
439 "Unsupported EF Type: " + std::to_string(efType));
440 }
441
442 return fileType;
443}
444
445} /* namespace calypso */
446} /* namespace card */
447} /* namespace keyple */