Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
SecureSymmetricCryptoTransactionManagerAdapter.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/SecureSymmetricCryptoTransactionManagerAdapter.hpp"
15
16#include <algorithm>
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/CommandChangeKey.hpp"
25#include "keyple/card/calypso/CommandChangePin.hpp"
26#include "keyple/card/calypso/CommandCloseSecureSession.hpp"
27#include "keyple/card/calypso/CommandGetChallenge.hpp"
28#include "keyple/card/calypso/CommandInvalidate.hpp"
29#include "keyple/card/calypso/CommandManageSession.hpp"
30#include "keyple/card/calypso/CommandOpenSecureSession.hpp"
31#include "keyple/card/calypso/CommandRatification.hpp"
32#include "keyple/card/calypso/CommandRehabilitate.hpp"
33#include "keyple/card/calypso/CommandSvDebitOrUndebit.hpp"
34#include "keyple/card/calypso/CommandSvGet.hpp"
35#include "keyple/card/calypso/CommandSvReload.hpp"
36#include "keyple/card/calypso/CommandVerifyPin.hpp"
37#include "keyple/core/plugin/CardIOException.hpp"
38#include "keyple/core/util/KeypleAssert.hpp"
39#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
40#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
41#include "keyple/core/util/cpp/exception/RuntimeException.hpp"
42#include "keyple/core/util/cpp/exception/UnsupportedOperationException.hpp"
43#include "keypop/calypso/card/transaction/CryptoIOException.hpp"
44#include "keypop/calypso/card/transaction/SessionBufferOverflowException.hpp"
45#include "keypop/calypso/card/transaction/UnexpectedCommandStatusException.hpp"
46#include "keypop/calypso/crypto/asymmetric/AsymmetricCryptoException.hpp"
47#include "keypop/calypso/crypto/legacysam/transaction/ReaderIOException.hpp"
48#include "keypop/calypso/crypto/symmetric/SymmetricCryptoException.hpp"
49#include "keypop/calypso/crypto/symmetric/SymmetricCryptoIOException.hpp"
50#include "keypop/reader/CardCommunicationException.hpp"
51#include "keypop/reader/CardReader.hpp"
52#include "keypop/reader/ReaderCommunicationException.hpp"
53#include "keypop/reader/selection/InvalidCardResponseException.hpp"
54
55namespace keyple {
56namespace card {
57namespace calypso {
58
59using keyple::card::calypso::CalypsoCardConstant;
60using keyple::core::plugin::CardIOException;
61using keyple::core::util::Assert;
62using keyple::core::util::cpp::exception::IllegalArgumentException;
63using keyple::core::util::cpp::exception::IllegalStateException;
64using keyple::core::util::cpp::exception::RuntimeException;
65using keyple::core::util::cpp::exception::UnsupportedOperationException;
66using keypop::calypso::card::transaction::CryptoIOException;
67using keypop::calypso::card::transaction::SessionBufferOverflowException;
68using keypop::calypso::card::transaction::UnexpectedCommandStatusException;
69using keypop::calypso::crypto::asymmetric::AsymmetricCryptoException;
70using keypop::calypso::crypto::legacysam::transaction::ReaderIOException;
71using keypop::calypso::crypto::symmetric::SymmetricCryptoException;
72using keypop::calypso::crypto::symmetric::SymmetricCryptoIOException;
73using keypop::reader::CardCommunicationException;
74using keypop::reader::CardReader;
75using keypop::reader::ReaderCommunicationException;
76using keypop::reader::selection::InvalidCardResponseException;
77
78template <typename T>
79const std::string
80 SecureSymmetricCryptoTransactionManagerAdapter<T>::MSG_PIN_NOT_AVAILABLE
81 = "PIN is not available for this card";
82template <typename T>
83const int SecureSymmetricCryptoTransactionManagerAdapter<
84 T>::SESSION_BUFFER_CMD_ADDITIONAL_COST = 6;
85template <typename T>
86const int SecureSymmetricCryptoTransactionManagerAdapter<T>::APDU_HEADER_LENGTH
87 = 5;
88
89template <typename T>
90SecureSymmetricCryptoTransactionManagerAdapter<T>::
91 SecureSymmetricCryptoTransactionManagerAdapter(
92 std::shared_ptr<ProxyReaderApi> cardReader,
93 std::shared_ptr<CalypsoCardAdapter> card,
94 std::shared_ptr<SymmetricCryptoSecuritySettingAdapter>
95 symmetricCryptoSecuritySetting)
96: TransactionManagerAdapter<T>(cardReader, card)
97, SecureTransactionManagerAdapter<T>(cardReader, card)
98, mSymmetricCryptoSecuritySetting(symmetricCryptoSecuritySetting)
99{
100 std::shared_ptr<SymmetricCryptoCardTransactionManagerFactorySpi>
101 cryptoFactory = symmetricCryptoSecuritySetting
102 ->getCryptoCardTransactionManagerFactorySpi();
103
104 /* Extended mode flag */
105 mIsExtendedMode = card->isExtendedModeSupported()
106 && cryptoFactory->isExtendedModeSupported();
107
108 /* Encryption is not active until a secure session enables it */
109 mIsEncryptionActive = false;
110
111 /* SV and postponed data tracking state */
112 mModificationsCounter = card->getModificationsCounter();
113 mNbPostponedData = 0;
114 mIsSvGet = false;
115 mSvOperation = SvOperation::RELOAD;
116 mSvAction = SvAction::DO;
117 mIsSvOperationInSecureSession = false;
118
119 if (!mIsExtendedMode) {
120 SecureTransactionManagerAdapter<T>::disablePreOpenMode();
121 }
122
123 /* Adjust card & SAM payload capacities */
124 mPayloadCapacity = std::min(
125 static_cast<int>(card->getPayloadCapacity()),
126 cryptoFactory->getMaxCardApduLengthSupported() - APDU_HEADER_LENGTH);
127
128 /* CL-SAM-CSN.1 */
129 mSymmetricCryptoCardTransactionManagerSpi
130 = cryptoFactory->createCardTransactionManager(
131 card->getCalypsoSerialNumberFull(),
132 mIsExtendedMode,
133 SecureTransactionManagerAdapter<T>::getTransactionAuditData());
134
135 mCryptoExtension
136 = std::dynamic_pointer_cast<CardTransactionCryptoExtension>(
137 mSymmetricCryptoCardTransactionManagerSpi);
138
139 mTransactionContext = std::make_shared<DtoAdapters::TransactionContextDto>(
140 card, mSymmetricCryptoCardTransactionManagerSpi);
141
142 mModificationsCounter = card->getModificationsCounter();
143}
144
145template <typename T>
146std::shared_ptr<DtoAdapters::TransactionContextDto>
147SecureSymmetricCryptoTransactionManagerAdapter<T>::getTransactionContext() const
148{
149 return mTransactionContext;
150}
151
152template <typename T>
153std::shared_ptr<DtoAdapters::CommandContextDto>
154SecureSymmetricCryptoTransactionManagerAdapter<T>::getCommandContext() const
155{
156 return std::make_shared<DtoAdapters::CommandContextDto>(
157 SecureTransactionManagerAdapter<T>::mIsSecureSessionOpen,
158 mIsEncryptionActive);
159}
160
161template <typename T>
162void
163SecureSymmetricCryptoTransactionManagerAdapter<T>::resetCommandContext()
164{
165 SecureTransactionManagerAdapter<T>::mIsSecureSessionOpen = false;
166 mIsEncryptionActive = false;
167}
168
169template <typename T>
170int
171SecureSymmetricCryptoTransactionManagerAdapter<T>::getPayloadCapacity() const
172{
173 return mPayloadCapacity;
174}
175
176template <typename T>
177void
178SecureSymmetricCryptoTransactionManagerAdapter<T>::resetTransaction()
179{
180 resetCommandContext();
181
182 mModificationsCounter
183 = SecureTransactionManagerAdapter<T>::mCard->getModificationsCounter();
184 mNbPostponedData = 0;
185 mSvPostponedDataIndex = -1;
186 mIsSvGet = false;
187 mSvOperation = SvOperation::RELOAD; // FIXME: might be a bad choice, we
188 // might need a pointer
189 mIsSvOperationInSecureSession = false;
190
191 SecureTransactionManagerAdapter<T>::disablePreOpenMode();
192
193 SecureTransactionManagerAdapter<T>::mCommands.clear();
194
195 if (mTransactionContext->isSecureSessionOpen()) {
196 try {
197 auto cancelSecureSessionCommand
198 = std::make_shared<CommandCloseSecureSession>(
199 mTransactionContext, getCommandContext(), true);
200 cancelSecureSessionCommand->finalizeRequest();
201
202 std::vector<std::shared_ptr<Command>> commands;
203 commands.push_back(cancelSecureSessionCommand);
204 SecureTransactionManagerAdapter<T>::executeCardCommands(
205 commands, ChannelControl::KEEP_OPEN);
206
207 } catch (const RuntimeException& e) {
208 mLogger->warn(
209 "Failed to abort secure session [reason=%]\n", e.getMessage());
210 }
211
212 /* Finally */
213 SecureTransactionManagerAdapter<T>::mCard->restoreFiles();
214 mTransactionContext->setSecureSessionOpen(false);
215 }
216}
217
218template <typename T>
219void
220SecureSymmetricCryptoTransactionManagerAdapter<
221 T>::prepareNewSecureSessionIfNeeded(const std::shared_ptr<Command>& command)
222{
223 if (!SecureTransactionManagerAdapter<T>::mIsSecureSessionOpen) {
224 return;
225 }
226
227 mModificationsCounter -= computeCommandSessionBufferSize(command);
228 if (mModificationsCounter < 0) {
229 checkMultipleSessionEnabled(command);
230 SecureTransactionManagerAdapter<T>::mCommands.push_back(
231 std::make_shared<CommandCloseSecureSession>(
232 mTransactionContext,
233 getCommandContext(),
234 true,
235 mSvPostponedDataIndex));
236
237 SecureTransactionManagerAdapter<T>::disablePreOpenMode();
238
239 SecureTransactionManagerAdapter<T>::mCommands.push_back(
240 std::make_shared<CommandOpenSecureSession>(
241 mTransactionContext,
242 getCommandContext(),
243 mSymmetricCryptoSecuritySetting,
244 mWriteAccessLevel,
245 mIsExtendedMode));
246
247 if (mIsEncryptionActive) {
248 auto session = std::make_shared<CommandManageSession>(
249 mTransactionContext, getCommandContext());
250 session->setEncryptionRequested(true);
251 SecureTransactionManagerAdapter<T>::mCommands.push_back(session);
252 }
253
254 mModificationsCounter = SecureTransactionManagerAdapter<T>::mCard
255 ->getModificationsCounter();
256 mModificationsCounter -= computeCommandSessionBufferSize(command);
257 mNbPostponedData = 0;
258 mSvPostponedDataIndex = -1;
259 mIsSvOperationInSecureSession = false;
260 }
261}
262
263template <typename T>
264int
265SecureSymmetricCryptoTransactionManagerAdapter<
266 T>::computeCommandSessionBufferSize(const std::shared_ptr<Command>& command)
267 const
268{
269 return SecureTransactionManagerAdapter<T>::mCard
270 ->isModificationsCounterInBytes()
271 ? static_cast<int>(command->getApduRequest()->getApdu().size())
272 + SESSION_BUFFER_CMD_ADDITIONAL_COST - APDU_HEADER_LENGTH
273 : 1;
274}
275
276template <typename T>
277void
278SecureSymmetricCryptoTransactionManagerAdapter<T>::checkMultipleSessionEnabled(
279 const std::shared_ptr<Command>& command) const
280{
281 /*
282 * CL-CSS-REQUEST.1
283 * CL-CSS-SMEXCEED.1
284 * CL-CSS-INFOCSS.1
285 */
286 if (!mSymmetricCryptoSecuritySetting->isMultipleSessionEnabled()) {
287 throw SessionBufferOverflowException(
288 std::string("Multiple session is not allowed. A command would ")
289 + "overflow the card modifications buffer. Command: "
290 + command->getName()
291 + SecureTransactionManagerAdapter<
292 T>::getTransactionAuditDataAsString(),
293 nullptr);
294 }
295}
296
297template <typename T>
298bool
299SecureSymmetricCryptoTransactionManagerAdapter<
300 T>::canConfigureReadOnOpenSecureSession() const
301{
302 return SecureTransactionManagerAdapter<T>::mIsSecureSessionOpen
303 && !mSymmetricCryptoSecuritySetting->isReadOnSessionOpeningDisabled()
304 && SecureTransactionManagerAdapter<T>::mCard
305 ->getPreOpenWriteAccessLevel()
306 == WriteAccessLevel::UNKOWN
307 && !SecureTransactionManagerAdapter<T>::mCommands.empty()
308 && SecureTransactionManagerAdapter<T>::mCommands
309 [SecureTransactionManagerAdapter<T>::mCommands.size() - 1]
310 ->getCommandRef()
311 == CardCommandRef::OPEN_SECURE_SESSION
312 && !std::dynamic_pointer_cast<CommandOpenSecureSession>(
313 SecureTransactionManagerAdapter<T>::mCommands
314 [SecureTransactionManagerAdapter<T>::mCommands.size()
315 - 1])
316 ->isReadModeConfigured();
317}
318
319template <typename T>
320T&
321SecureSymmetricCryptoTransactionManagerAdapter<T>::
322 prepareIncreaseOrDecreaseCounter(
323 bool isDecreaseCommand,
324 std::uint8_t sfi,
325 int counterNumber,
326 int incDecValue)
327{
328 SecureTransactionManagerAdapter<T>::prepareIncreaseOrDecreaseCounter(
329 isDecreaseCommand, sfi, counterNumber, incDecValue);
330
331 return dynamic_cast<T&>(*this);
332}
333
334// template <typename T>
335// T&
336// SecureSymmetricCryptoTransactionManagerAdapter<T>::processCommands(
337// keypop::calypso::card::transaction::ChannelControl channelControl)
338// {
339// try {
340// return processCommands(
341// keypop::reader::valueOf(static_cast<int>(channelControl)));
342//
343// } catch (const CardCommunicationException& e) {
344// throw CardIOException(e.what(), Exception(e.what()));
345//
346// } catch (const ReaderCommunicationException& e) {
347// throw ReaderIOException(e.what(), e);
348//
349// } catch (const InvalidCardResponseException& e) {
350// throw UnexpectedCommandStatusException(e.what(), e);
351// }
352// }
353
354template <typename T>
355T&
356SecureSymmetricCryptoTransactionManagerAdapter<T>::processCommands(
357 ChannelControl channelControl)
358{
359 if (SecureTransactionManagerAdapter<T>::mCommands.empty()) {
360 processCryptoPreparedCommands();
361 return dynamic_cast<T&>(*this);
362 }
363
364 try {
365 std::vector<std::shared_ptr<Command>> cardRequestCommands;
366
367 for (const auto& command :
368 SecureTransactionManagerAdapter<T>::mCommands) {
369 if (command->isCryptoServiceRequiredToFinalizeRequest()
370 && (!synchronizeCryptoServiceBeforeCardProcessing(
371 cardRequestCommands))) {
372 SecureTransactionManagerAdapter<T>::executeCardCommands(
373 cardRequestCommands, ChannelControl::KEEP_OPEN);
374 cardRequestCommands.clear();
375 }
376
377 command->finalizeRequest();
378 cardRequestCommands.push_back(command);
379 }
380
381 SecureTransactionManagerAdapter<T>::executeCardCommands(
382 cardRequestCommands, channelControl);
383 processCryptoPreparedCommands();
384
385 } catch (...) {
386 resetTransaction();
387
388 /* Finally */
389 SecureTransactionManagerAdapter<T>::mCommands.clear();
390 if (mIsExtendedMode
391 && !SecureTransactionManagerAdapter<T>::mCard
392 ->isExtendedModeSupported()) {
393 mIsExtendedMode = false;
394 }
395
396 throw;
397 }
398
399 /* Finally */
400 SecureTransactionManagerAdapter<T>::mCommands.clear();
401 if (mIsExtendedMode
402 && !SecureTransactionManagerAdapter<T>::mCard
403 ->isExtendedModeSupported()) {
404 mIsExtendedMode = false;
405 }
406
407 return dynamic_cast<T&>(*this);
408}
409
410template <typename T>
411void
412SecureSymmetricCryptoTransactionManagerAdapter<T>::handleCommandPostProcessing(
413 int commandIndex, const std::vector<std::shared_ptr<Command>>& commands)
414{
415 /* Only a card that really postpones the counter value produces postponed
416 * data; an unknown behaviour, i.e. a null pointer, counts as "no". */
417 const auto isCounterValuePostponed
418 = mTransactionContext->getCard()->getIsCounterValuePostponed();
419
420 if (isCounterValuePostponed == nullptr || !*isCounterValuePostponed) {
421 return;
422 }
423
424 CardCommandRef commandRef = commands[commandIndex]->getCommandRef();
425
426 if (commandRef != CardCommandRef::INCREASE
427 && commandRef != CardCommandRef::DECREASE) {
428 return;
429 }
430
431 mNbPostponedData++;
432 if (commandIndex == static_cast<int>(commands.size() - 1)) {
433 return;
434 }
435
436 bool isSv = false;
437
438 for (int i = commandIndex + 1; i < static_cast<int>(commands.size()); i++) {
439 commandRef = commands[i]->getCommandRef();
440 if (commandRef == CardCommandRef::SV_RELOAD
441 || commandRef == CardCommandRef::SV_DEBIT
442 || commandRef == CardCommandRef::SV_UNDEBIT) {
443 isSv = true;
444
445 } else if (commandRef == CardCommandRef::CLOSE_SECURE_SESSION) {
446 if (isSv) {
447 const auto session
448 = std::dynamic_pointer_cast<CommandCloseSecureSession>(
449 commands[i]);
450
451 if (session != nullptr) {
452 session->incrementSvPostponedDataIndex();
453 }
454 }
455 break;
456 }
457 }
458}
459
460template <typename T>
461bool
462SecureSymmetricCryptoTransactionManagerAdapter<T>::
463 synchronizeCryptoServiceBeforeCardProcessing(
464 const std::vector<std::shared_ptr<Command>>& commands)
465{
466 for (const auto& command : commands) {
467 if (!command->synchronizeCryptoServiceBeforeCardProcessing()) {
468 return false;
469 }
470 }
471
472 return true;
473}
474
475template <typename T>
476void
477SecureSymmetricCryptoTransactionManagerAdapter<
478 T>::processCryptoPreparedCommands()
479{
480 if (mSymmetricCryptoCardTransactionManagerSpi != nullptr) {
481 try {
482 mSymmetricCryptoCardTransactionManagerSpi->synchronize();
483
484 } catch (const SymmetricCryptoException& e) {
485 throw CryptoException(e.what(), e);
486
487 } catch (const SymmetricCryptoIOException& e) {
488 throw CryptoIOException(e.what(), e);
489 }
490 }
491}
492
493template <typename T>
494T&
495SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareVerifyPin(
496 const std::vector<std::uint8_t>& pin)
497{
498 try {
499 Assert::getInstance().isEqual(
500 pin.size(), CalypsoCardConstant::PIN_LENGTH, "PIN length");
501
502 if (!SecureTransactionManagerAdapter<T>::mCard
503 ->isPinFeatureAvailable()) {
504 throw UnsupportedOperationException(MSG_PIN_NOT_AVAILABLE);
505 }
506
507 if (mSymmetricCryptoSecuritySetting == nullptr
508 || mSymmetricCryptoSecuritySetting
509 ->isPinPlainTransmissionEnabled()) {
510 SecureTransactionManagerAdapter<T>::mCommands.push_back(
511 std::unique_ptr<CommandVerifyPin>(new CommandVerifyPin(
512 getTransactionContext(), getCommandContext(), pin)));
513
514 } else {
515 /*
516 * CL-PIN-PENCRYPT.1
517 * CL-PIN-GETCHAL.1
518 */
519 SecureTransactionManagerAdapter<T>::mCommands.push_back(
520 std::make_shared<CommandGetChallenge>(
521 getTransactionContext(), getCommandContext()));
522 SecureTransactionManagerAdapter<T>::mCommands.push_back(
523 std::make_shared<CommandVerifyPin>(
524 getTransactionContext(),
525 getCommandContext(),
526 pin,
527 *mSymmetricCryptoSecuritySetting
528 ->getPinVerificationCipheringKif(),
529 *mSymmetricCryptoSecuritySetting
530 ->getPinVerificationCipheringKvc()));
531 }
532
533 } catch (...) {
534 resetTransaction();
535 throw;
536 }
537
538 return dynamic_cast<T&>(*this);
539}
540
541template <typename T>
542T&
543SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareChangePin(
544 const std::vector<std::uint8_t>& newPin)
545{
546 try {
547 Assert::getInstance().isEqual(
548 newPin.size(), CalypsoCardConstant::PIN_LENGTH, "PIN length");
549
550 if (!SecureTransactionManagerAdapter<T>::mCard
551 ->isPinFeatureAvailable()) {
552 throw UnsupportedOperationException(MSG_PIN_NOT_AVAILABLE);
553 }
554
555 SecureTransactionManagerAdapter<T>::checkNoSecureSession();
556
557 /* CL-PIN-MENCRYPT.1 */
558 if (mSymmetricCryptoSecuritySetting == nullptr
559 || mSymmetricCryptoSecuritySetting
560 ->isPinPlainTransmissionEnabled()) {
561 SecureTransactionManagerAdapter<T>::mCommands.push_back(
562 std::make_shared<CommandChangePin>(
563 getTransactionContext(), getCommandContext(), newPin));
564
565 } else {
566 /* CL-PIN-GETCHAL.1 */
567 SecureTransactionManagerAdapter<T>::mCommands.push_back(
568 std::make_shared<CommandGetChallenge>(
569 getTransactionContext(), getCommandContext()));
570 SecureTransactionManagerAdapter<T>::mCommands.push_back(
571 std::make_shared<CommandChangePin>(
572 getTransactionContext(),
573 getCommandContext(),
574 newPin,
575 *mSymmetricCryptoSecuritySetting
576 ->getPinModificationCipheringKif(),
577 *mSymmetricCryptoSecuritySetting
578 ->getPinModificationCipheringKvc()));
579 }
580
581 } catch (...) {
582 resetTransaction();
583 throw;
584 }
585
586 return dynamic_cast<T&>(*this);
587}
588
589template <typename T>
590std::shared_ptr<CardTransactionCryptoExtension>
591SecureSymmetricCryptoTransactionManagerAdapter<T>::getCryptoExtension()
592{
593 return mCryptoExtension;
594}
595
596template <typename T>
597T&
598SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareOpenSecureSession(
599 WriteAccessLevel writeAccessLevel)
600{
601 try {
602 SecureTransactionManagerAdapter<T>::checkNoSecureSession();
603
604 if (SecureTransactionManagerAdapter<T>::mCard
605 ->getPreOpenWriteAccessLevel()
606 != WriteAccessLevel::UNKOWN
607 && SecureTransactionManagerAdapter<T>::mCard
608 ->getPreOpenWriteAccessLevel()
609 != writeAccessLevel) {
610 mLogger->warn(
611 std::string("Pre-open mode cancelled because writeAccessLevel")
612 + "mismatches writeAccessLevel used for pre-open mode "
613 + "[writeAccessLevel=%, preOpenWriteAccessLevel=%]\n",
614 std::to_string(static_cast<int>(writeAccessLevel)),
615 std::to_string(
616 static_cast<int>(SecureTransactionManagerAdapter<T>::mCard
617 ->getPreOpenWriteAccessLevel())));
618
619 SecureTransactionManagerAdapter<T>::disablePreOpenMode();
620 }
621
622 SecureTransactionManagerAdapter<T>::mCommands.push_back(
623 std::make_shared<CommandOpenSecureSession>(
624 mTransactionContext,
625 getCommandContext(),
626 mSymmetricCryptoSecuritySetting,
627 writeAccessLevel,
628 mIsExtendedMode));
629
630 mWriteAccessLevel = writeAccessLevel; /* CL-KEY-INDEXPO.1 */
631 SecureTransactionManagerAdapter<T>::mIsSecureSessionOpen = true;
632 mIsEncryptionActive = false;
633 mModificationsCounter = SecureTransactionManagerAdapter<T>::mCard
634 ->getModificationsCounter();
635 mNbPostponedData = 0;
636 mSvPostponedDataIndex = -1;
637 mIsSvOperationInSecureSession = false;
638
639 } catch (...) {
640 resetTransaction();
641 throw;
642 }
643
644 return dynamic_cast<T&>(*this);
645}
646
647template <typename T>
648T&
649SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareCloseSecureSession()
650{
651 try {
652 SecureTransactionManagerAdapter<T>::checkSecureSession();
653
654 auto reader = std::dynamic_pointer_cast<CardReader>(
655 SecureTransactionManagerAdapter<T>::mCardReader);
656 if (mSymmetricCryptoSecuritySetting->isRatificationMechanismEnabled()
657 && reader != nullptr && reader->isContactless()) {
658 /*
659 * CL-RAT-CMD.1
660 * CL-RAT-DELAY.1
661 * CL-RAT-NXTCLOSE.1
662 */
663 SecureTransactionManagerAdapter<T>::mCommands.push_back(
664 std::unique_ptr<CommandCloseSecureSession>(
665 new CommandCloseSecureSession(
666 getTransactionContext(),
667 getCommandContext(),
668 false,
669 mSvPostponedDataIndex)));
670 SecureTransactionManagerAdapter<T>::mCommands.push_back(
671 std::unique_ptr<CommandRatification>(new CommandRatification(
672 getTransactionContext(), getCommandContext())));
673
674 } else {
675 SecureTransactionManagerAdapter<T>::mCommands.push_back(
676 std::unique_ptr<CommandCloseSecureSession>(
677 new CommandCloseSecureSession(
678 getTransactionContext(),
679 getCommandContext(),
680 true,
681 mSvPostponedDataIndex)));
682 }
683
684 } catch (...) {
685 resetTransaction();
686
687 /* Finally */
688 resetCommandContext();
689 SecureTransactionManagerAdapter<T>::disablePreOpenMode();
690
691 throw;
692 }
693
694 /* Finally */
695 resetCommandContext();
696 SecureTransactionManagerAdapter<T>::disablePreOpenMode();
697
698 return dynamic_cast<T&>(*this);
699}
700
701template <typename T>
702T&
703SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareSvGet(
704 SvOperation svOperation, SvAction svAction)
705{
706 try {
707 if (!SecureTransactionManagerAdapter<T>::mCard
708 ->isSvFeatureAvailable()) {
709 throw UnsupportedOperationException(
710 "Stored Value is not available for this card");
711 }
712
713 if (mSymmetricCryptoSecuritySetting->isSvLoadAndDebitLogEnabled()
714 && !mIsExtendedMode) {
715 /*
716 * @see Calypso Layer ID 8.09/8.10 (200108): both reload and debit
717 * logs are requested for a non rev3.2 card add two SvGet commands
718 * (for RELOAD then for DEBIT).
719 * CL-SV-GETNUMBER.1
720 */
721 SvOperation operation1 = svOperation == SvOperation::RELOAD
722 ? SvOperation::DEBIT
723 : SvOperation::RELOAD;
724
725 SecureTransactionManagerAdapter<T>::mCommands.push_back(
726 std::unique_ptr<CommandSvGet>(new CommandSvGet(
727 mTransactionContext,
728 getCommandContext(),
729 operation1,
730 false)));
731 }
732
733 SecureTransactionManagerAdapter<T>::mCommands.push_back(
734 std::unique_ptr<CommandSvGet>(new CommandSvGet(
735 mTransactionContext,
736 getCommandContext(),
737 svOperation,
738 mIsExtendedMode)));
739
740 mIsSvGet = true;
741 mSvOperation = svOperation;
742 mSvAction = svAction;
743
744 } catch (...) {
745 resetTransaction();
746 throw;
747 }
748
749 return dynamic_cast<T&>(*this);
750}
751
752template <typename T>
753T&
754SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareSvReload(
755 int amount,
756 const std::vector<std::uint8_t>& date,
757 const std::vector<std::uint8_t>& time,
758 const std::vector<std::uint8_t>& free)
759{
760 try {
761 /*
762 * FIXME: Assert::isInRange() only takes size_t bounds, so comparing
763 * against the negative CalypsoCardConstant::SV_LOAD_MIN_VALUE wraps
764 * it around to a huge unsigned value and makes every amount
765 * (including valid ones) fail the lower-bound check. Checked
766 * manually here instead of going through Assert until isInRange()
767 * gains a signed overload.
768 */
769 if (amount < CalypsoCardConstant::SV_LOAD_MIN_VALUE
770 || amount > CalypsoCardConstant::SV_LOAD_MAX_VALUE) {
771 throw IllegalArgumentException(
772 "Argument [amount] has a value [" + std::to_string(amount)
773 + "] out of range ["
774 + std::to_string(CalypsoCardConstant::SV_LOAD_MIN_VALUE) + ".."
775 + std::to_string(CalypsoCardConstant::SV_LOAD_MAX_VALUE)
776 + "].");
777 }
778
779 Assert::getInstance()
780 .isEqual(date.size(), 2, "date")
781 .isEqual(time.size(), 2, "time")
782 .isEqual(free.size(), 2, "free");
783
784 checkSvModifyingCommandPreconditions(SvOperation::RELOAD);
785
786 auto command = std::make_shared<CommandSvReload>(
787 mTransactionContext,
788 getCommandContext(),
789 amount,
790 date,
791 time,
792 free,
793 mIsExtendedMode);
794
795 prepareNewSecureSessionIfNeeded(command);
796 SecureTransactionManagerAdapter<T>::mCommands.push_back(command);
797
798 } catch (...) {
799 resetTransaction();
800 throw;
801 }
802
803 return dynamic_cast<T&>(*this);
804}
805
806template <typename T>
807void
808SecureSymmetricCryptoTransactionManagerAdapter<
809 T>::checkSvModifyingCommandPreconditions(SvOperation svOperation)
810{
811 /*
812 * CL-SV-GETDEBIT.1
813 * CL-SV-GETRLOAD.1
814 */
815 if (!mIsSvGet) {
816 throw IllegalStateException(
817 "SV modifying command must follow an SV Get command");
818 }
819
820 mIsSvGet = false;
821 if (svOperation != mSvOperation) {
822 throw IllegalStateException(
823 std::string("SV operation is inconsistent with previous SV Get")
824 + " command. Expected: "
825 + std::to_string(static_cast<int>(mSvOperation))
826 + ", Actual: " + std::to_string(static_cast<int>(svOperation)));
827 }
828
829 /* CL-SV-1PCSS.1 */
830 if (SecureTransactionManagerAdapter<T>::mIsSecureSessionOpen) {
831 if (mIsSvOperationInSecureSession) {
832 throw IllegalStateException(
833 "Only one SV modifying command is allowed per Secure Session");
834 }
835
836 mIsSvOperationInSecureSession = true;
837 mSvPostponedDataIndex = mNbPostponedData;
838 mNbPostponedData++;
839 }
840}
841
842template <typename T>
843T&
844SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareSvReload(int amount)
845{
846 const std::vector<std::uint8_t> zero = {0x00, 0x00};
847 prepareSvReload(amount, zero, zero, zero);
848
849 return dynamic_cast<T&>(*this);
850}
851
852template <typename T>
853T&
854SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareSvDebit(
855 int amount,
856 const std::vector<std::uint8_t>& date,
857 const std::vector<std::uint8_t>& time)
858{
859 try {
860 /* @see Calypso Layer ID 8.02 (200108) */
861
862 /* CL-SV-DEBITVAL.1 */
863 Assert::getInstance()
864 .isInRange(
865 amount,
866 CalypsoCardConstant::SV_DEBIT_MIN_VALUE,
867 CalypsoCardConstant::SV_DEBIT_MAX_VALUE,
868 "amount")
869 .isEqual(date.size(), 2, "date")
870 .isEqual(time.size(), 2, "time");
871
872 checkSvModifyingCommandPreconditions(SvOperation::DEBIT);
873
874 auto command = std::make_shared<CommandSvDebitOrUndebit>(
875 mSvAction == SvAction::DO,
876 mTransactionContext,
877 getCommandContext(),
878 amount,
879 date,
880 time,
881 mIsExtendedMode,
882 mSymmetricCryptoSecuritySetting->isSvNegativeBalanceAuthorized());
883
884 prepareNewSecureSessionIfNeeded(command);
885 SecureTransactionManagerAdapter<T>::mCommands.push_back(command);
886
887 } catch (...) {
888 resetTransaction();
889 throw;
890 }
891
892 return dynamic_cast<T&>(*this);
893}
894
895template <typename T>
896T&
897SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareSvDebit(int amount)
898{
899 const std::vector<std::uint8_t> zero = {0x00, 0x00};
900
901 prepareSvDebit(amount, zero, zero);
902
903 return dynamic_cast<T&>(*this);
904}
905
906template <typename T>
907T&
908SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareInvalidate()
909{
910 try {
911 if (SecureTransactionManagerAdapter<T>::mCard->isDfInvalidated()) {
912 throw IllegalStateException("Card is already invalidated");
913 }
914
915 auto command = std::make_shared<CommandInvalidate>(
916 mTransactionContext, getCommandContext());
917
918 prepareNewSecureSessionIfNeeded(command);
919 SecureTransactionManagerAdapter<T>::mCommands.push_back(command);
920
921 } catch (...) {
922 resetTransaction();
923 throw;
924 }
925
926 return dynamic_cast<T&>(*this);
927}
928
929template <typename T>
930T&
931SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareRehabilitate()
932{
933 try {
934 if (!SecureTransactionManagerAdapter<T>::mCard->isDfInvalidated()) {
935 throw IllegalStateException("Card is not invalidated");
936 }
937
938 auto command = std::make_shared<CommandRehabilitate>(
939 mTransactionContext, getCommandContext());
940
941 prepareNewSecureSessionIfNeeded(command);
942 SecureTransactionManagerAdapter<T>::mCommands.push_back(command);
943
944 } catch (...) {
945 resetTransaction();
946 throw;
947 }
948
949 return dynamic_cast<T&>(*this);
950}
951
952template <typename T>
953T&
954SecureSymmetricCryptoTransactionManagerAdapter<T>::prepareChangeKey(
955 int keyIndex,
956 std::uint8_t newKif,
957 std::uint8_t newKvc,
958 std::uint8_t issuerKif,
959 std::uint8_t issuerKvc)
960{
961 try {
962 if (SecureTransactionManagerAdapter<T>::mCard->getProductType()
963 == CalypsoCard::ProductType::BASIC) {
964 throw UnsupportedOperationException(
965 "'Change Key' command is not available for this card");
966 }
967
968 SecureTransactionManagerAdapter<T>::checkNoSecureSession();
969 Assert::getInstance().isInRange(keyIndex, 1, 3, "keyIndex");
970
971 /* CL-KEY-CHANGE.1 */
972 SecureTransactionManagerAdapter<T>::mCommands.push_back(
973 std::unique_ptr<CommandGetChallenge>(new CommandGetChallenge(
974 mTransactionContext, getCommandContext())));
975 SecureTransactionManagerAdapter<T>::mCommands.push_back(
976 std::unique_ptr<CommandChangeKey>(new CommandChangeKey(
977 mTransactionContext,
978 getCommandContext(),
979 static_cast<std::uint8_t>(keyIndex),
980 newKif,
981 newKvc,
982 issuerKif,
983 issuerKvc)));
984
985 } catch (...) {
986 resetTransaction();
987 throw;
988 }
989
990 return dynamic_cast<T&>(*this);
991}
992
993} /* namespace calypso */
994} /* namespace card */
995} /* namespace keyple */
996
997/* Explicit template instantiations for concrete transaction manager bases */
998#include "keypop/calypso/card/cpp/SecureExtendedModeTransactionManagerBase.hpp"
999#include "keypop/calypso/card/cpp/SecureRegularModeTransactionManagerBase.hpp"
1000
1001template class keyple::card::calypso::
1002 SecureSymmetricCryptoTransactionManagerAdapter<
1003 keypop::calypso::card::cpp::SecureExtendedModeTransactionManagerBase>;
1004
1005template class keyple::card::calypso::
1006 SecureSymmetricCryptoTransactionManagerAdapter<
1007 keypop::calypso::card::cpp::SecureRegularModeTransactionManagerBase>;