Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandIncreaseOrDecreaseMultiple.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/CommandIncreaseOrDecreaseMultiple.hpp"
15
16#include <map>
17#include <memory>
18#include <sstream>
19#include <string>
20#include <vector>
21
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/CardDataOutOfBoundsException.hpp"
28#include "keyple/card/calypso/CardIllegalParameterException.hpp"
29#include "keyple/card/calypso/CardSecurityContextException.hpp"
30#include "keyple/card/calypso/CardSessionBufferOverflowException.hpp"
31#include "keyple/core/util/ApduUtil.hpp"
32#include "keyple/core/util/ByteArrayUtil.hpp"
33#include "keyple/core/util/HexUtil.hpp"
34#include "keyple/core/util/cpp/Arrays.hpp"
35#include "keyple/core/util/cpp/MapUtils.hpp"
36#include "keyple/core/util/cpp/System.hpp"
37#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
38
39namespace keyple {
40namespace card {
41namespace calypso {
42
43using keyple::core::util::ApduUtil;
44using keyple::core::util::ByteArrayUtil;
45using keyple::core::util::HexUtil;
46using keyple::core::util::cpp::Arrays;
47using keyple::core::util::cpp::MapUtils;
48using keyple::core::util::cpp::System;
49using keyple::core::util::cpp::exception::IllegalStateException;
50
51const std::map<int, const std::shared_ptr<Command::StatusProperties>>
52 CommandIncreaseOrDecreaseMultiple::STATUS_TABLE = [] {
53 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
54 Command::STATUS_TABLE);
55
56 m.insert(
57 {{0x6400,
58 std::make_shared<Command::StatusProperties>(
59 "Too many modifications in session",
60 typeid(CardSessionBufferOverflowException))},
61 {0x6700,
62 std::make_shared<Command::StatusProperties>(
63 "Lc value not supported", typeid(CardDataAccessException))},
64 {0x6981,
65 std::make_shared<Command::StatusProperties>(
66 "The current EF is not a Counters or Simulated Counter EF",
67 typeid(CardDataAccessException))},
68 {0x6982,
69 std::make_shared<Command::StatusProperties>(
70 "Security conditions not fulfilled (no secure session, "
71 "incorrect key, encryption required, PKI mode and not Always "
72 "access mode)",
73 typeid(CardSecurityContextException))},
74 {0x6985,
75 std::make_shared<Command::StatusProperties>(
76 "Access forbidden (Never access mode, DF is invalidated, "
77 "etc.)",
78 typeid(CardAccessForbiddenException))},
79 {0x6986,
80 std::make_shared<Command::StatusProperties>(
81 "Incorrect file type: the Current File is not an EF. "
82 "Supersedes 6981h",
83 typeid(CardDataAccessException))},
84 {0x6A80,
85 std::make_shared<Command::StatusProperties>(
86 "Incorrect command data (Overflow error, Incorrect counter "
87 "number, Counter number present more than once)",
88 typeid(CardIllegalParameterException))},
89 {0x6A82,
90 std::make_shared<Command::StatusProperties>(
91 "File not found", typeid(CardDataAccessException))},
92 {0x6B00,
93 std::make_shared<Command::StatusProperties>(
94 "P1 or P2 value not supported",
95 typeid(CardIllegalParameterException))}});
96 return m;
97 }();
98
99CommandIncreaseOrDecreaseMultiple::CommandIncreaseOrDecreaseMultiple(
100 bool isDecreaseCommand,
101 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
102 transactionContext,
103 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
104 std::uint8_t sfi,
105 const std::map<int, int>& counterNumberToIncDecValueMap)
106: Command(
107 isDecreaseCommand ? CardCommandRef::DECREASE_MULTIPLE
108 : CardCommandRef::INCREASE_MULTIPLE,
109 std::unique_ptr<int>(
110 new int(static_cast<int>(counterNumberToIncDecValueMap.size()) * 4)),
111 transactionContext,
112 commandContext)
113, mSfi(sfi)
114, mCounterNumberToIncDecValueMap(counterNumberToIncDecValueMap)
115{
116 const std::uint8_t p1 = 0;
117 const std::uint8_t p2 = (sfi * 8);
118 std::vector<std::uint8_t> dataIn(4 * counterNumberToIncDecValueMap.size());
119 int index = 0;
120
121 for (const auto& entry : counterNumberToIncDecValueMap) {
122 dataIn[index] = static_cast<std::uint8_t>(entry.first);
123 int incDecValue = entry.second;
124 ByteArrayUtil::copyBytes(incDecValue, dataIn, index + 1, 3);
125 index += 4;
126 }
127
128 /* APDU Case 4 */
129 setApduRequest(
130 std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
131 transactionContext->getCard()->getCardClass().getValue(),
132 getCommandRef().getInstructionByte(),
133 p1,
134 p2,
135 dataIn,
136 0x00)));
137
138 std::stringstream extraInfo;
139 extraInfo << "SFI:" << HexUtil::toHex(sfi);
140 for (const auto& entry : counterNumberToIncDecValueMap) {
141 extraInfo << ", ";
142 extraInfo << entry.first << ": " << entry.second;
143 }
144
145 addSubName(extraInfo.str());
146}
147
148void
149CommandIncreaseOrDecreaseMultiple::finalizeRequest()
150{
151 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
152}
153
154bool
155CommandIncreaseOrDecreaseMultiple::isCryptoServiceRequiredToFinalizeRequest()
156 const
157{
158 return getCommandContext()->isEncryptionActive();
159}
160
161bool
162CommandIncreaseOrDecreaseMultiple ::
163 synchronizeCryptoServiceBeforeCardProcessing()
164{
165 if (getCommandContext()->isEncryptionActive()) {
166 return false;
167 }
168
169 updateTerminalSessionIfNeeded(buildAnticipatedResponse());
170
171 return true;
172}
173
174void
175CommandIncreaseOrDecreaseMultiple::parseResponse(
176 std::shared_ptr<ApduResponseApi> apduResponse)
177{
178 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
179 Command::setApduResponseAndCheckStatus(apduResponse);
180
181 if (apduResponse->getDataOut().size() > 0) {
182 const std::vector<std::uint8_t> dataOut = apduResponse->getDataOut();
183 int nbCounters = static_cast<int>(dataOut.size()) / 4;
184 for (int i = 0; i < nbCounters; i++) {
185 getTransactionContext()->getCard()->setCounter(
186 mSfi,
187 dataOut[i * 4] & 0xFF,
188 Arrays::copyOfRange(dataOut, (i * 4) + 1, (i * 4) + 4));
189 }
190 }
191
192 updateTerminalSessionIfNeeded();
193}
194
195const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
196CommandIncreaseOrDecreaseMultiple::getStatusTable() const
197{
198 return STATUS_TABLE;
199}
200
201std::vector<std::uint8_t>
202CommandIncreaseOrDecreaseMultiple::buildAnticipatedResponse()
203{
204 /* Response = CCVVVVVV..CCVVVVVV9000 */
205 std::map<const int, const int> oldCounterValues = getOldCounterValues();
206 std::vector<std::uint8_t> response(
207 2 + (mCounterNumberToIncDecValueMap.size() * 4));
208 int index = 0;
209
210 for (const auto& entry : mCounterNumberToIncDecValueMap) {
211 response[index] = static_cast<std::uint8_t>(entry.first);
212 int newCounterValue;
213
214 if (getCommandRef() == CardCommandRef::DECREASE_MULTIPLE) {
215 newCounterValue = oldCounterValues[entry.first] - entry.second;
216 } else {
217 newCounterValue = oldCounterValues[entry.first] + entry.second;
218 }
219
220 ByteArrayUtil::copyBytes(newCounterValue, response, index + 1, 3);
221 index += 4;
222 }
223
224 /* SW 9000 */
225 response[index] = 0x90;
226 response[index + 1] = 0x00;
227
228 return response;
229}
230
231std::map<const int, const int>
232CommandIncreaseOrDecreaseMultiple::getOldCounterValues()
233{
234 std::shared_ptr<ElementaryFile> ef
235 = getTransactionContext()->getCard()->getFileBySfi(mSfi);
236
237 if (ef != nullptr) {
238 const std::map<const int, const int> allCountersValue
239 = ef->getData()->getAllCountersValue();
240
241 const std::vector<int> allCountersValueKeySet
242 = MapUtils::getKeySet(allCountersValue);
243 const std::vector<int> counterNumberToIncDecValueKeySet
244 = MapUtils::getKeySet(mCounterNumberToIncDecValueMap);
245
246 if (Arrays::containsAll(
247 allCountersValueKeySet, counterNumberToIncDecValueKeySet)) {
248 return allCountersValue;
249 }
250 }
251
252 throw new IllegalStateException(
253 std::string("Unable to determine anticipated APDU response ")
254 + "because some expected counters have not been read beforehand. "
255 + "Command: " + getName() + ", SFI: " + HexUtil::toHex(mSfi) + "h");
256}
257
258} /* namespace calypso */
259} /* namespace card */
260} /* namespace keyple */