Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandSvReload.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/CommandSvReload.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/CalypsoCardConstant.hpp"
23#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
24#include "keyple/card/calypso/CardIllegalParameterException.hpp"
25#include "keyple/card/calypso/CardSecurityDataException.hpp"
26#include "keyple/card/calypso/CardSessionBufferOverflowException.hpp"
27#include "keyple/card/calypso/CardTerminatedException.hpp"
28#include "keyple/core/util/ApduUtil.hpp"
29#include "keyple/core/util/ByteArrayUtil.hpp"
30#include "keyple/core/util/cpp/Arrays.hpp"
31#include "keyple/core/util/cpp/System.hpp"
32#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
33#include "keypop/calypso/card/transaction/CardSignatureNotVerifiableException.hpp"
34#include "keypop/calypso/card/transaction/CryptoException.hpp"
35#include "keypop/calypso/card/transaction/CryptoIOException.hpp"
36#include "keypop/calypso/card/transaction/InvalidCardSignatureException.hpp"
37#include "keypop/calypso/crypto/symmetric/SymmetricCryptoException.hpp"
38#include "keypop/calypso/crypto/symmetric/SymmetricCryptoIOException.hpp"
39
40namespace keyple {
41namespace card {
42namespace calypso {
43
44using keyple::core::util::ApduUtil;
45using keyple::core::util::ByteArrayUtil;
46using keyple::core::util::cpp::Arrays;
47using keyple::core::util::cpp::System;
48using keyple::core::util::cpp::exception::IllegalStateException;
49using keypop::calypso::card::transaction::CardSignatureNotVerifiableException;
50using keypop::calypso::card::transaction::CryptoException;
51using keypop::calypso::card::transaction::CryptoIOException;
52using keypop::calypso::card::transaction::InvalidCardSignatureException;
53using keypop::calypso::crypto::symmetric::SymmetricCryptoException;
54using keypop::calypso::crypto::symmetric::SymmetricCryptoIOException;
55
56const std::string CommandSvReload::MSG_CARD_SV_MAC_NOT_VERIFIABLE
57 = "Unable to verify the card SV MAC associated to the SV operation";
58const std::string CommandSvReload::MSG_INVALID_CARD_SESSION_MAC
59 = "Invalid card session MAC";
60const int CommandSvReload::SW_POSTPONED_DATA = 0x6200;
61
62const std::map<int, const std::shared_ptr<Command::StatusProperties>>
63 CommandSvReload::STATUS_TABLE = [] {
64 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
65 Command::STATUS_TABLE);
66
67 m.insert(
68 {{0x6400,
69 std::make_shared<StatusProperties>(
70 "Too many modifications in session",
71 typeid(CardSessionBufferOverflowException))},
72 {0x6700,
73 std::make_shared<StatusProperties>(
74 "Lc value not supported",
75 typeid(CardIllegalParameterException))},
76 {0x6900,
77 std::make_shared<StatusProperties>(
78 "Transaction counter is 0 or SV TNum is FFFEh or FFFFh",
79 typeid(CardTerminatedException))},
80 {0x6985,
81 std::make_shared<StatusProperties>(
82 "Preconditions not satisfied",
83 typeid(CardAccessForbiddenException))},
84 {0x6988,
85 std::make_shared<StatusProperties>(
86 "Incorrect signatureHi", typeid(CardSecurityDataException))},
87 {SW_POSTPONED_DATA,
88 std::make_shared<StatusProperties>(
89 "Successful execution, response data postponed until session "
90 "closing")}});
91 return m;
92 }();
93
94CommandSvReload::CommandSvReload(
95 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
96 transactionContext,
97 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
98 int amount,
99 const std::vector<std::uint8_t>& date,
100 const std::vector<std::uint8_t>& time,
101 const std::vector<std::uint8_t>& free,
102 bool isExtendedModeAllowed)
103: Command(
104 CardCommandRef::SV_RELOAD,
105 std::unique_ptr<int>(new int(computeExpectedResponseLength(
106 commandContext, isExtendedModeAllowed))),
107 transactionContext,
108 commandContext)
109/* Keeps a copy of these fields until the builder is finalized */
110, mAmount(amount)
111, mIsExtendedModeAllowed(isExtendedModeAllowed)
112{
113 /*
114 * Handle the dataIn size with signatureHi length according to card revision
115 * (3.2 rev have a 10-byte signature)
116 */
117 mDataIn = std::vector<std::uint8_t>(18 + (isExtendedModeAllowed ? 10 : 5));
118
119 /* dataIn[0] will be filled in at the finalization phase. */
120 mDataIn[1] = date[0];
121 mDataIn[2] = date[1];
122 mDataIn[3] = free[0];
123 mDataIn[4] = transactionContext->getCard()->getSvKvc();
124 mDataIn[5] = free[1];
125 ByteArrayUtil::copyBytes(amount, mDataIn, 6, 3);
126 mDataIn[9] = time[0];
127 mDataIn[10] = time[1];
128
129 /*
130 * dataIn[11]..dataIn[11+7+sigLen] will be filled in at the finalization
131 * phase. Add dummy apdu request to ensure it exists when checking the
132 * session buffer usage.
133 * APDU Case 3 (in session) or 4 (outside session)
134 */
135 std::unique_ptr<std::uint8_t> le(
136 computeLe(commandContext, isExtendedModeAllowed));
137
138 std::vector<std::uint8_t> apdu;
139 if (le == nullptr) {
140 apdu = ApduUtil::build(0, 0, 0, 0, mDataIn);
141
142 } else {
143 apdu = ApduUtil::build(0, 0, 0, 0, mDataIn, *le.get());
144 }
145
146 setApduRequest(std::make_shared<DtoAdapters::ApduRequestAdapter>(apdu));
147}
148
149void
150CommandSvReload::finalizeRequest()
151{
152 auto svCommandSecurityData(
153 std::make_shared<DtoAdapters::SvCommandSecurityDataApiAdapter>());
154 svCommandSecurityData->setSvGetRequest(
155 getTransactionContext()->getCard()->getSvGetHeader());
156 svCommandSecurityData->setSvGetResponse(
157 getTransactionContext()->getCard()->getSvGetData());
158 svCommandSecurityData->setSvCommandPartialRequest(getSvReloadData());
159
160 try {
161 getTransactionContext()
162 ->getSymmetricCryptoCardTransactionManagerSpi()
163 ->computeSvCommandSecurityData(svCommandSecurityData);
164
165 } catch (const SymmetricCryptoException& e) {
166 throw CryptoException(e.what(), e);
167
168 } catch (const SymmetricCryptoIOException& e) {
169 throw CryptoIOException(e.what(), e);
170 }
171
172 finalizeCommand(svCommandSecurityData);
173 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
174}
175
176bool
177CommandSvReload::isCryptoServiceRequiredToFinalizeRequest() const
178{
179 return true;
180}
181
182bool
183CommandSvReload::synchronizeCryptoServiceBeforeCardProcessing()
184{
185 return false;
186}
187
188void
189CommandSvReload::parseResponse(std::shared_ptr<ApduResponseApi> apduResponse)
190{
191 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
192 Command::setApduResponseAndCheckStatus(apduResponse);
193
194 if (apduResponse->getDataOut().size() != 0
195 && apduResponse->getDataOut().size() != 3
196 && apduResponse->getDataOut().size() != 6) {
197 throw IllegalStateException(
198 "SV Reload response is not the correct length. Expected: 0/3/6, "
199 "Actual: "
200 + std::to_string(apduResponse->getDataOut().size()));
201 }
202
203 std::shared_ptr<CalypsoCardAdapter> calypsoCard
204 = getTransactionContext()->getCard();
205 calypsoCard->setSvOperationSignature(apduResponse->getDataOut());
206 updateCalypsoCardSvHistory(calypsoCard);
207 updateTerminalSessionIfNeeded();
208
209 if (!getCommandContext()->isSecureSessionOpen()) {
210 try {
211 if (!getTransactionContext()
212 ->getSymmetricCryptoCardTransactionManagerSpi()
213 ->isCardSvMacValid(
214 getTransactionContext()
215 ->getCard()
216 ->getSvOperationSignature())) {
217 throw InvalidCardSignatureException(
218 MSG_INVALID_CARD_SESSION_MAC);
219 }
220
221 } catch (const SymmetricCryptoIOException& e) {
222 throw CardSignatureNotVerifiableException(
223 MSG_CARD_SV_MAC_NOT_VERIFIABLE, e);
224
225 } catch (const SymmetricCryptoException& e) {
226 throw CryptoIOException(e.what(), e);
227 }
228 }
229}
230
231const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
232CommandSvReload::getStatusTable() const
233{
234 return STATUS_TABLE;
235}
236
237int
238CommandSvReload::computeExpectedResponseLength(
239 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
240 bool isExtendedModeAllowed)
241{
242 if (commandContext->isSecureSessionOpen()) {
243 return 0;
244 } else {
245 return isExtendedModeAllowed ? 6 : 3;
246 }
247}
248
249std::unique_ptr<std::uint8_t>
250CommandSvReload::computeLe(
251 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
252 bool isExtendedModeAllowed)
253{
254 if (commandContext->isSecureSessionOpen()) {
255 return nullptr;
256 } else {
257 return std::unique_ptr<std::uint8_t>(
258 new std::uint8_t(isExtendedModeAllowed ? 6 : 3));
259 }
260}
261
262void
263CommandSvReload::finalizeCommand(
264 std::shared_ptr<DtoAdapters::SvCommandSecurityDataApiAdapter>
265 svCommandSecurityData)
266{
267 const std::uint8_t p1 = svCommandSecurityData->getTerminalChallenge()[0];
268 std::uint8_t p2 = svCommandSecurityData->getTerminalChallenge()[1];
269 mDataIn[0] = svCommandSecurityData->getTerminalChallenge()[2];
270 System::arraycopy(
271 svCommandSecurityData->getSerialNumber(), 0, mDataIn, 11, 4);
272 System::arraycopy(
273 svCommandSecurityData->getTransactionNumber(), 0, mDataIn, 15, 3);
274 System::arraycopy(
275 svCommandSecurityData->getTerminalSvMac(),
276 0,
277 mDataIn,
278 18,
279 svCommandSecurityData->getTerminalSvMac().size());
280
281 /* APDU Case 3 (in session) or 4 (outside session) */
282 const std::unique_ptr<std::uint8_t> le(
283 computeLe(getCommandContext(), mIsExtendedModeAllowed));
284
285 std::vector<std::uint8_t> _apdu;
286 if (le == nullptr)
287 {
288 _apdu = ApduUtil::build(
289 getTransactionContext()->getCard()->getCardClass()
290 == CalypsoCardClass::LEGACY
291 ? CalypsoCardClass::LEGACY_STORED_VALUE.getValue()
292 : CalypsoCardClass::ISO.getValue(),
293 getCommandRef().getInstructionByte(),
294 p1,
295 p2,
296 mDataIn);
297 }
298 else
299 {
300 _apdu = ApduUtil::build(
301 getTransactionContext()->getCard()->getCardClass()
302 == CalypsoCardClass::LEGACY
303 ? CalypsoCardClass::LEGACY_STORED_VALUE.getValue()
304 : CalypsoCardClass::ISO.getValue(),
305 getCommandRef().getInstructionByte(),
306 p1,
307 p2,
308 mDataIn,
309 *le);
310 }
311
312 auto apdu(std::make_shared<DtoAdapters::ApduRequestAdapter>(_apdu));
313 apdu->addSuccessfulStatusWord(SW_POSTPONED_DATA);
314 setApduRequest(apdu);
315}
316
317std::vector<std::uint8_t>
318CommandSvReload::getSvReloadData()
319{
320 std::vector<std::uint8_t> svReloadData(15);
321 svReloadData[0] = getCommandRef().getInstructionByte();
322
323 /*
324 * svReloadData[1,2] / P1P2 not set because ignored
325 * Lc is 5 bytes longer in revision 3.2
326 */
327 svReloadData[3] = mIsExtendedModeAllowed ? 0x1C : 0x17;
328
329 /* appends the fixed part of dataIn */
330 System::arraycopy(mDataIn, 0, svReloadData, 4, 11);
331
332 return svReloadData;
333}
334
335void
336CommandSvReload::updateCalypsoCardSvHistory(
337 std::shared_ptr<CalypsoCardAdapter> calypsoCard)
338{
339 const int balance = calypsoCard->getSvBalance() + mAmount;
340 calypsoCard->updateSvData(balance, calypsoCard->getSvLastTNum() + 1);
341
342 std::vector<std::uint8_t> reloadLog(22);
343 System::arraycopy(getApduRequest()->getApdu(), 6, reloadLog, 0, 5);
344 ByteArrayUtil::copyBytes(balance, reloadLog, 5, 3);
345 ByteArrayUtil::copyBytes(mAmount, reloadLog, 8, 3);
346 System::arraycopy(getApduRequest()->getApdu(), 14, reloadLog, 11, 9);
347 ByteArrayUtil::copyBytes(calypsoCard->getSvLastTNum(), reloadLog, 20, 2);
348 calypsoCard->addCyclicContent(
349 CalypsoCardConstant::SV_RELOAD_LOG_FILE_SFI, reloadLog);
350}
351
352} /* namespace calypso */
353} /* namespace card */
354} /* namespace keyple */