Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandIncreaseOrDecrease.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/CommandIncreaseOrDecrease.hpp"
15
16#include <map>
17#include <memory>
18#include <string>
19#include <vector>
20
21#include "keyple/card/calypso/CalypsoCardAdapter.hpp"
22#include "keyple/card/calypso/CalypsoCardClass.hpp"
23#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
24#include "keyple/card/calypso/CardCommandRef.hpp"
25#include "keyple/card/calypso/CardDataAccessException.hpp"
26#include "keyple/card/calypso/CardDataOutOfBoundsException.hpp"
27#include "keyple/card/calypso/CardIllegalParameterException.hpp"
28#include "keyple/card/calypso/CardSecurityContextException.hpp"
29#include "keyple/card/calypso/CardSessionBufferOverflowException.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/IllegalStateException.hpp"
36
37namespace keyple {
38namespace card {
39namespace calypso {
40
41using keyple::core::util::ApduUtil;
42using keyple::core::util::ByteArrayUtil;
43using keyple::core::util::HexUtil;
44using keyple::core::util::cpp::Arrays;
45using keyple::core::util::cpp::System;
46using keyple::core::util::cpp::exception::IllegalStateException;
47
48const int CommandIncreaseOrDecrease::SW_POSTPONED_DATA = 0x6200;
49
50const std::map<int, const std::shared_ptr<Command::StatusProperties>>
51 CommandIncreaseOrDecrease::STATUS_TABLE = [] {
52 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
53 Command::STATUS_TABLE);
54
55 m.insert(
56 {{0x6400,
57 std::make_shared<Command::StatusProperties>(
58 "Too many modifications in session",
59 typeid(CardSessionBufferOverflowException))},
60 {0x6700,
61 std::make_shared<Command::StatusProperties>(
62 "Lc value not supported", typeid(CardDataAccessException))},
63 {0x6981,
64 std::make_shared<Command::StatusProperties>(
65 "The current EF is not a Counters or Simulated Counter EF",
66 typeid(CardDataAccessException))},
67 {0x6982,
68 std::make_shared<Command::StatusProperties>(
69 "Security conditions not fulfilled (no session, wrong key, "
70 "encryption required)",
71 typeid(CardSecurityContextException))},
72 {0x6985,
73 std::make_shared<Command::StatusProperties>(
74 "Access forbidden (Never access mode, DF is invalidated, "
75 "etc..)",
76 typeid(CardAccessForbiddenException))},
77 {0x6986,
78 std::make_shared<Command::StatusProperties>(
79 "Command not allowed (no current EF)",
80 typeid(CardDataAccessException))},
81 {0x6A80,
82 std::make_shared<Command::StatusProperties>(
83 "Overflow error", typeid(CardDataOutOfBoundsException))},
84 {0x6A82,
85 std::make_shared<Command::StatusProperties>(
86 "File not found", typeid(CardDataAccessException))},
87 {0x6B00,
88 std::make_shared<Command::StatusProperties>(
89 "P1 or P2 value not supported",
90 typeid(CardIllegalParameterException))},
91 {0x6103,
92 std::make_shared<Command::StatusProperties>(
93 "Successful execution (possible only in ISO7816 T=0)",
94 typeid(CardIllegalParameterException))},
95 {SW_POSTPONED_DATA,
96 std::make_shared<Command::StatusProperties>(
97 "Successful execution, response data postponed until session "
98 "closing",
99 typeid(CardIllegalParameterException))}});
100 return m;
101 }();
102
103CommandIncreaseOrDecrease::CommandIncreaseOrDecrease(
104 bool isDecreaseCommand,
105 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
106 transactionContext,
107 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
108 std::uint8_t sfi,
109 int counterNumber,
110 int incDecValue)
111: Command(
112 isDecreaseCommand ? CardCommandRef::DECREASE : CardCommandRef::INCREASE,
113 std::unique_ptr<int>(new int(3)),
114 transactionContext,
115 commandContext)
116, mSfi(sfi)
117, mCounterNumber(counterNumber)
118, mIncDecValue(incDecValue)
119{
120 /*
121 * Convert the integer value into a 3-byte buffer
122 * CL-COUN-DATAIN.1
123 */
124 const std::vector<std::uint8_t> valueBuffer
125 = ByteArrayUtil::extractBytes(incDecValue, 3);
126
127 const std::uint8_t p2 = (sfi * 8);
128
129 std::shared_ptr<DtoAdapters::ApduRequestAdapter> apduRequest;
130
131 if (transactionContext->getCard()->getIsCounterValuePostponed() != nullptr
132 && *(transactionContext->getCard()->getIsCounterValuePostponed())
133 == false) {
134 /*
135 * For Rev 3 cards or legacy cards which not postpone data, this is a
136 * case4 command; We set Le = 0.
137 */
138 apduRequest
139 = std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
140 transactionContext->getCard()->getCardClass().getValue(),
141 getCommandRef().getInstructionByte(),
142 static_cast<uint8_t>(counterNumber),
143 p2,
144 valueBuffer,
145 0));
146 } else {
147 /*
148 * For legacy cards, this command is considered as case 3 (especially to
149 * support postponed data); We set Le = null.
150 */
151 apduRequest
152 = std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
153 transactionContext->getCard()->getCardClass().getValue(),
154 getCommandRef().getInstructionByte(),
155 static_cast<uint8_t>(counterNumber),
156 p2,
157 valueBuffer));
158
159 apduRequest->addSuccessfulStatusWord(SW_POSTPONED_DATA);
160 }
161
162 setApduRequest(apduRequest);
163
164 std::stringstream extraInfo;
165 extraInfo << "SFI:" << sfi << "h, "
166 << "Counter:" << counterNumber << ","
167 << (isDecreaseCommand ? "Decrement" : "Increment") << ": "
168 << incDecValue;
169
170 addSubName(extraInfo.str());
171}
172
173void
174CommandIncreaseOrDecrease::finalizeRequest()
175{
176 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
177}
178
179bool
180CommandIncreaseOrDecrease::isCryptoServiceRequiredToFinalizeRequest() const
181{
182 return getCommandContext()->isEncryptionActive();
183}
184
185bool
186CommandIncreaseOrDecrease::synchronizeCryptoServiceBeforeCardProcessing()
187{
188 if (getCommandContext()->isEncryptionActive()
189 || getTransactionContext()->getCard()->getIsCounterValuePostponed()
190 == nullptr) {
191 return false;
192 }
193
194 updateTerminalSessionIfNeeded(buildAnticipatedResponse());
195
196 return true;
197}
198
199void
200CommandIncreaseOrDecrease::parseResponse(
201 std::shared_ptr<ApduResponseApi> apduResponse)
202{
203 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
204 if (apduResponse->getDataOut().size() == 0
205 && (getTransactionContext()->getCard()->getIsCounterValuePostponed()
206 == nullptr
207 || *(getTransactionContext()
208 ->getCard()
209 ->getIsCounterValuePostponed())
210 == true)) {
211 setExpectedResponseLength(0);
212 }
213
214 Command::setApduResponseAndCheckStatus(apduResponse);
215
216 if (apduResponse->getStatusWord() == SW_POSTPONED_DATA) {
217 getTransactionContext()->getCard()->setIsCounterValuePostponed(true);
218 getTransactionContext()->getCard()->setCounter(
219 mSfi,
220 static_cast<uint8_t>(mCounterNumber != 0 ? mCounterNumber : 1),
221 buildAnticipatedDataOut());
222
223 } else {
224 getTransactionContext()->getCard()->setIsCounterValuePostponed(false);
225 getTransactionContext()->getCard()->setCounter(
226 mSfi,
227 static_cast<uint8_t>(mCounterNumber != 0 ? mCounterNumber : 1),
228 apduResponse->getDataOut());
229 }
230
231 updateTerminalSessionIfNeeded();
232}
233
234const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
235CommandIncreaseOrDecrease::getStatusTable() const
236{
237 return STATUS_TABLE;
238}
239
240std::vector<std::uint8_t>
241CommandIncreaseOrDecrease::buildAnticipatedResponse()
242{
243 std::vector<std::uint8_t> response;
244 if (getTransactionContext()->getCard()->getIsCounterValuePostponed()
245 != nullptr
246 && *(getTransactionContext()->getCard()->getIsCounterValuePostponed())
247 == true) {
248 /* Response = 6200 */
249 response.resize(2);
250 response[0] = 0x62; /* SW 6200 */
251 response[1] = 0x00;
252 } else {
253 /* Response = NNNNNN9000 */
254 const std::vector<std::uint8_t> dataOut = buildAnticipatedDataOut();
255 response.resize(5);
256 response[0] = dataOut[0];
257 response[1] = dataOut[1];
258 response[2] = dataOut[2];
259 response[3] = 0x90; /* SW 9000 */
260 response[4] = 0x00;
261 }
262
263 return response;
264}
265
266std::vector<std::uint8_t>
267CommandIncreaseOrDecrease::buildAnticipatedDataOut()
268{
269 const std::shared_ptr<ElementaryFile> ef
270 = getTransactionContext()->getCard()->getFileBySfi(mSfi);
271
272 if (ef != nullptr) {
273 std::shared_ptr<int> oldCounterValue
274 = ef->getData()->getContentAsCounterValue(
275 mCounterNumber != 0 ? mCounterNumber : 1);
276 if (oldCounterValue != nullptr) {
277 return ByteArrayUtil::extractBytes(
278 getCommandRef() == CardCommandRef::DECREASE
279 ? *oldCounterValue - mIncDecValue
280 : *oldCounterValue + mIncDecValue,
281 3);
282 }
283 }
284
285 throw IllegalStateException(
286 std::string("Unable to determine anticipated APDU response ")
287 + "because the counter has not been read beforehand. "
288 + "Command: " + getName() + ", SFI: " + HexUtil::toHex(mSfi)
289 + "h, Counter: " + std::to_string(mCounterNumber));
290}
291
292} /* namespace calypso */
293} /* namespace card */
294} /* namespace keyple */