Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
Command.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/Command.hpp"
15
16#include <map>
17#include <memory>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "keyple/card/calypso/CalypsoCardConstant.hpp"
23#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
24#include "keyple/card/calypso/CardDataAccessException.hpp"
25#include "keyple/card/calypso/CardDataOutOfBoundsException.hpp"
26#include "keyple/card/calypso/CardIllegalArgumentException.hpp"
27#include "keyple/card/calypso/CardIllegalParameterException.hpp"
28#include "keyple/card/calypso/CardPinException.hpp"
29#include "keyple/card/calypso/CardSecurityContextException.hpp"
30#include "keyple/card/calypso/CardSecurityDataException.hpp"
31#include "keyple/card/calypso/CardSessionBufferOverflowException.hpp"
32#include "keyple/card/calypso/CardTerminatedException.hpp"
33#include "keyple/card/calypso/CardUnexpectedResponseLengthException.hpp"
34#include "keyple/card/calypso/CardUnknownStatusException.hpp"
35#include "keyple/core/util/cpp/System.hpp"
36#include "keypop/calypso/card/transaction/CryptoException.hpp"
37#include "keypop/calypso/card/transaction/CryptoIOException.hpp"
38#include "keypop/calypso/crypto/asymmetric/AsymmetricCryptoException.hpp"
39#include "keypop/calypso/crypto/symmetric/SymmetricCryptoException.hpp"
40#include "keypop/calypso/crypto/symmetric/SymmetricCryptoIOException.hpp"
41
42namespace keyple {
43namespace card {
44namespace calypso {
45
46using keyple::core::util::cpp::System;
47using keypop::calypso::card::transaction::CryptoException;
48using keypop::calypso::card::transaction::CryptoIOException;
49using keypop::calypso::crypto::asymmetric::AsymmetricCryptoException;
50using keypop::calypso::crypto::symmetric::SymmetricCryptoException;
51using keypop::calypso::crypto::symmetric::SymmetricCryptoIOException;
52
53const std::vector<std::uint8_t> Command::APDU_RESPONSE_9000 = {0x90, 0x00};
54
55const std::map<int, const std::shared_ptr<Command::StatusProperties>>
56 Command::STATUS_TABLE = [] {
57 /* Do not copy Command::STATUS_TABLE here: it is the object being
58 * initialized (undefined behaviour, crashes at DLL load with MSVC). */
59 std::map<int, const std::shared_ptr<Command::StatusProperties>> m;
60
61 m.insert(
62 {{0x9000, std::make_shared<Command::StatusProperties>("Success")}});
63 return m;
64 }();
65
66Command::Command(
67 const CardCommandRef& commandRef,
68 std::unique_ptr<int> expectedResponseLength,
69 std::shared_ptr<DtoAdapters::TransactionContextDto> transactionContext,
70 std::shared_ptr<DtoAdapters::CommandContextDto> commandContext)
71: mCommandRef(commandRef)
72, mCommandContext(commandContext)
73, mTransactionContext(transactionContext)
74, mExpectedResponseLength(std::move(expectedResponseLength))
75, mName(commandRef.getName())
76, mIsCryptoServiceSynchronized(false)
77{
78}
79
80void
81Command::addSubName(const std::string& subName)
82{
83 mName += (" - " + subName);
84 mApduRequest->setInfo(mName);
85}
86
87const CardCommandRef&
88Command::getCommandRef() const
89{
90 return mCommandRef;
91}
92
93const std::string&
94Command::getName() const
95{
96 return mName;
97}
98
99void
100Command::setApduRequest(
101 std::shared_ptr<DtoAdapters::ApduRequestAdapter> apduRequest)
102{
103 mApduRequest = apduRequest;
104 mApduRequest->setInfo(mName);
105}
106
107void
108Command::setApduRequestInBestEffortMode(
109 std::shared_ptr<DtoAdapters::ApduRequestAdapter> apduRequest)
110{
111 setApduRequest(apduRequest);
112 if (mCommandContext->isSecureSessionOpen()) {
113 apduRequest
114 ->addSuccessfulStatusWord(CalypsoCardConstant::SW_FILE_NOT_FOUND)
115 .addSuccessfulStatusWord(CalypsoCardConstant::SW_RECORD_NOT_FOUND);
116 }
117}
118
119std::shared_ptr<DtoAdapters::ApduRequestAdapter>
120Command::getApduRequest() const
121{
122 return mApduRequest;
123}
124
125std::shared_ptr<ApduResponseApi>
126Command::getApduResponse() const
127{
128 return mApduResponse;
129}
130
131std::shared_ptr<DtoAdapters::TransactionContextDto>
132Command::getTransactionContext() const
133{
134 return mTransactionContext;
135}
136
137std::shared_ptr<DtoAdapters::CommandContextDto>
138Command::getCommandContext() const
139{
140 return mCommandContext;
141}
142
143void
144Command::setExpectedResponseLength(std::unique_ptr<int> expectedResponseLength)
145{
146 mExpectedResponseLength = std::move(expectedResponseLength);
147}
148
149int*
150Command::getExpectedResponseLength() const
151{
152 return mExpectedResponseLength.get();
153}
154
155void
156Command::confirmCryptoServiceSuccessfullySynchronized()
157{
158 mIsCryptoServiceSynchronized = true;
159}
160
161bool
162Command::isCryptoServiceSynchronized() const
163{
164 return mIsCryptoServiceSynchronized;
165}
166
167void
168Command::parseResponseForSelection(
169 const std::shared_ptr<ApduResponseApi>& apduResponse,
170 std::shared_ptr<CalypsoCardAdapter> calypsoCard)
171{
172 mTransactionContext->setCard(calypsoCard);
173 parseResponse(apduResponse);
174}
175
176void
177Command::updateTerminalSessionIfNeeded()
178{
179 updateTerminalSessionIfNeeded(mApduResponse->getApdu());
180}
181
182void
183Command::updateTerminalSessionIfNeeded(
184 const std::vector<std::uint8_t>& apduResponse)
185{
186 if (mIsCryptoServiceSynchronized) {
187 return;
188 }
189
190 if (mCommandContext->isSecureSessionOpen()) {
191 if (mTransactionContext->isPkiMode()) {
192 /* Asymmetric crypto mode */
193 std::shared_ptr<AsymmetricCryptoCardTransactionManagerSpi>
194 asymmetricCryptoCardTransactionManagerSpi(
195 mTransactionContext
196 ->getAsymmetricCryptoCardTransactionManagerSpi());
197 try {
198 asymmetricCryptoCardTransactionManagerSpi
199 ->updateTerminalPkiSession(mApduRequest->getApdu());
200 asymmetricCryptoCardTransactionManagerSpi
201 ->updateTerminalPkiSession(apduResponse);
202
203 } catch (const AsymmetricCryptoException& e) {
204 throw CryptoException(e.what(), e);
205 }
206 } else {
207 /* Symmetric crypto mode */
208 std::shared_ptr<SymmetricCryptoCardTransactionManagerSpi>
209 symmetricCryptoCardTransactionManager(
210 mTransactionContext
211 ->getSymmetricCryptoCardTransactionManagerSpi());
212
213 try {
214 symmetricCryptoCardTransactionManager->updateTerminalSessionMac(
215 mApduRequest->getApdu());
216 symmetricCryptoCardTransactionManager->updateTerminalSessionMac(
217 apduResponse);
218
219 } catch (const SymmetricCryptoException& e) {
220 throw CryptoException(e.what(), e);
221
222 } catch (const SymmetricCryptoIOException& e) {
223 throw CryptoIOException(e.what(), e);
224 }
225 }
226 }
227
228 mIsCryptoServiceSynchronized = true;
229}
230
231void
232Command::encryptRequestAndUpdateTerminalSessionMacIfNeeded()
233{
234 if (mCommandContext->isEncryptionActive()) {
235 try {
236 mApduRequest->setApdu(
237 mTransactionContext
238 ->getSymmetricCryptoCardTransactionManagerSpi()
239 ->updateTerminalSessionMac(mApduRequest->getApdu()));
240
241 } catch (const SymmetricCryptoException& e) {
242 throw CryptoException(e.what(), e);
243
244 } catch (const SymmetricCryptoIOException& e) {
245 throw CryptoIOException(e.what(), e);
246 }
247 }
248}
249
250void
251Command::decryptResponseAndUpdateTerminalSessionMacIfNeeded(
252 std::shared_ptr<ApduResponseApi> apduResponse)
253{
254 if (mCommandContext->isEncryptionActive()) {
255 try {
256 const std::vector<std::uint8_t> decryptedApdu
257 = mTransactionContext
258 ->getSymmetricCryptoCardTransactionManagerSpi()
259 ->updateTerminalSessionMac(apduResponse->getApdu());
260
261 apduResponse->setApdu(decryptedApdu);
262
263 } catch (const SymmetricCryptoException& e) {
264 throw CryptoException(e.what(), e);
265
266 } catch (const SymmetricCryptoIOException& e) {
267 throw CryptoIOException(e.what(), e);
268 }
269
270 mIsCryptoServiceSynchronized = true;
271 }
272}
273
274void
275Command::setApduResponseAndCheckStatus(
276 std::shared_ptr<ApduResponseApi> apduResponse)
277{
278 mApduResponse = apduResponse;
279
280 checkStatus();
281}
282
283bool
284Command::setApduResponseAndCheckStatusInBestEffortMode(
285 std::shared_ptr<ApduResponseApi> apduResponse)
286{
287 mApduResponse = apduResponse;
288
289 try {
290 checkStatus();
291
292 } catch (const CardDataAccessException&) {
293 if (mCommandContext->isSecureSessionOpen()
294 || (apduResponse->getStatusWord()
295 != CalypsoCardConstant::SW_FILE_NOT_FOUND
296 && apduResponse->getStatusWord()
297 != CalypsoCardConstant::SW_RECORD_NOT_FOUND)) {
298 throw;
299 }
300
301 return false;
302 }
303
304 return true;
305}
306
307const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
308Command::getStatusTable() const
309{
310 return STATUS_TABLE;
311}
312
313std::shared_ptr<Command::StatusProperties>
314Command::getStatusWordProperties() const
315{
316 const auto statusTable = getStatusTable();
317 auto it = statusTable.find(mApduResponse->getStatusWord());
318
319 return it != statusTable.end() ? it->second : nullptr;
320}
321
322void
323Command::checkStatus()
324{
325 std::shared_ptr<StatusProperties> props = getStatusWordProperties();
326
327 if (props != nullptr && props->isSuccessful()) {
328 /* SW is successful, then check the response length (CL-CSS-RESPLE.1) */
329 if (mExpectedResponseLength != nullptr
330 && static_cast<int>(mApduResponse->getDataOut().size())
331 != *mExpectedResponseLength) {
332 throw CardUnexpectedResponseLengthException(
333 "APDU response is not the expected length. Command: "
334 + mCommandRef.getName() + ", "
335 + "Expected: " + std::to_string(*mExpectedResponseLength)
336 + ", " + "Actual: "
337 + std::to_string(mApduResponse->getDataOut().size()),
338 mCommandRef);
339 }
340
341 /* SW and response length are correct. */
342 return;
343 }
344
345 /* status word is not referenced, or not successful. */
346
347 /* Exception class */
348 const std::type_info& exceptionClass
349 = props != nullptr ? props->getExceptionClass() : typeid(nullptr);
350
351 /* Message */
352 const std::string message
353 = props != nullptr ? props->getInformation() : "Unknown status";
354
355 /* Throw the exception */
356 throwCommandException(exceptionClass, message);
357}
358
359void
360Command::throwCommandException(
361 const std::type_info& exceptionClass, const std::string& message)
362{
363 if (exceptionClass == typeid(CardAccessForbiddenException)) {
364 throw CardAccessForbiddenException(message, mCommandRef);
365 } else if (exceptionClass == typeid(CardDataAccessException)) {
366 throw CardDataAccessException(message, mCommandRef);
367 } else if (exceptionClass == typeid(CardDataOutOfBoundsException)) {
368 throw CardDataOutOfBoundsException(message, mCommandRef);
369 } else if (exceptionClass == typeid(CardIllegalArgumentException)) {
370 throw CardIllegalArgumentException(message, mCommandRef);
371 } else if (exceptionClass == typeid(CardIllegalParameterException)) {
372 throw CardIllegalParameterException(message, mCommandRef);
373 } else if (exceptionClass == typeid(CardPinException)) {
374 throw CardPinException(message, mCommandRef);
375 } else if (exceptionClass == typeid(CardSecurityContextException)) {
376 throw CardSecurityContextException(message, mCommandRef);
377 } else if (exceptionClass == typeid(CardSecurityDataException)) {
378 throw CardSecurityDataException(message, mCommandRef);
379 } else if (exceptionClass == typeid(CardSessionBufferOverflowException)) {
380 throw CardSessionBufferOverflowException(message, mCommandRef);
381 } else if (exceptionClass == typeid(CardTerminatedException)) {
382 throw CardTerminatedException(message, mCommandRef);
383 } else {
384 throw CardUnknownStatusException(message, mCommandRef);
385 }
386}
387
388Command::StatusProperties::StatusProperties(const std::string& information)
389: mInformation(information)
390, mSuccessful(true)
391, mExceptionClass(typeid(nullptr))
392{
393}
394
395Command::StatusProperties::StatusProperties(
396 const std::string& information, const std::type_info& exceptionClass)
397: mInformation(information)
398, mSuccessful(exceptionClass == typeid(nullptr))
399, mExceptionClass(exceptionClass)
400{
401}
402
403const std::string&
404Command::StatusProperties::getInformation() const
405{
406 return mInformation;
407}
408
409bool
410Command::StatusProperties::isSuccessful() const
411{
412 return mSuccessful;
413}
414
415const std::type_info&
416Command::StatusProperties::getExceptionClass() const
417{
418 return mExceptionClass;
419}
420
421} /* namespace calypso */
422} /* namespace card */
423} /* namespace keyple */