Keyple Card Calypso C++ Library 2.2.5.6
Reference Terminal Reader API for C++
CommonSamTransactionManagerAdapter.h
Go to the documentation of this file.
1/**************************************************************************************************
2 * Copyright (c) 2023 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional information regarding *
5 * copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the terms of the Eclipse *
8 * Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 *
9 * *
10 * SPDX-License-Identifier: EPL-2.0 *
11 **************************************************************************************************/
12
13#pragma once
14
15/* Calypsonet Terminal Calypso */
16#include "InconsistentDataException.h"
17#include "InvalidCardSignatureException.h"
18#include "InvalidSignatureException.h"
19#include "ReaderIOException.h"
20#include "SamIOException.h"
21#include "SamRevokedException.h"
22#include "UnexpectedCommandStatusException.h"
23
24/* Calypsonet Terminal Card */
25#include "ProxyReaderApi.h"
26
27/* Keyple Card Calypso */
28#include "AbstractSamCommand.h"
30#include "CalypsoSamAdapter.h"
33#include "CardBrokenCommunicationException.h"
34#include "CardRequestAdapter.h"
35#include "CmdSamDataCipher.h"
40#include "CommonSignatureComputationData.h"
41#include "CommonSignatureVerificationData.h"
43#include "ReaderBrokenCommunicationException.h"
44#include "SamSecuritySetting.h"
46#include "SamTransactionManager.h"
49#include "UnexpectedStatusWordException.h"
50
51/* Keyple Core Util */
52#include "Arrays.h"
53#include "ByteArrayUtil.h"
54#include "HexUtil.h"
55#include "KeypleAssert.h"
56#include "KeypleStd.h"
57#include "LoggerFactory.h"
58#include "StringUtils.h"
59#include "UnsupportedOperationException.h"
60
61namespace keyple {
62namespace card {
63namespace calypso {
64
65using namespace calypsonet::terminal::card;
66using namespace keyple::core::util::cpp;
67using namespace keyple::core::util::cpp::exception;
68
75template <typename T>
77: public CommonTransactionManagerAdapter<SamTransactionManager,
78 CommonSecuritySetting,
79 CommonSecuritySetting>,
80 public SamTransactionManager {
81public:
93 const std::shared_ptr<ProxyReaderApi> samReader,
94 const std::shared_ptr<CalypsoSamAdapter> sam,
95 const std::shared_ptr<SamSecuritySettingAdapter> securitySetting)
97 sam,
98 securitySetting,
99 std::vector<std::vector<uint8_t>>()),
100 mSamReader(samReader),
101 mSam(sam),
102 mSecuritySetting(securitySetting),
103 mDefaultKeyDiversifier(sam->getSerialNumber()) {}
104
118 const std::shared_ptr<SmartCard> targetSmartCard,
119 const std::shared_ptr<CommonSecuritySettingAdapter<T>> securitySetting,
120 const std::vector<uint8_t>& defaultKeyDiversifier,
121 const std::vector<std::vector<uint8_t>>& transactionAuditData)
123 targetSmartCard,
124 securitySetting,
125 transactionAuditData),
126 mSamReader(securitySetting->getControlSamReader()),
127 mSam(securitySetting->getControlSam()),
128 mSecuritySetting(securitySetting),
129 mDefaultKeyDiversifier(defaultKeyDiversifier) {}
130
137 const std::vector<std::vector<uint8_t>>& getTransactionAuditData() const final
138 {
140 }
141
147 const std::shared_ptr<CardReader> getSamReader() const final
148 {
149 return std::dynamic_pointer_cast<CardReader>(mSamReader);
150 }
151
157 const std::shared_ptr<CalypsoSam> getCalypsoSam() const final
158 {
159 return mSam;
160 }
161
167 SamTransactionManager& prepareComputeSignature(const any data) override
168 {
169 /* C++: careful, code is a little bit different from Java because of any_cast flow */
170 try {
171
172 /* Basic signature */
173 auto dataAdapter =
174 any_cast<std::shared_ptr<BasicSignatureComputationDataAdapter>>(data);
175
176 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
177 .isInRange(dataAdapter->getData().size(),
178 1,
179 208,
180 "length of data to sign")
181 .isTrue(dataAdapter->getData().size() % 8 == 0,
182 "length of data to sign is a multiple of 8")
183 .isInRange(dataAdapter->getSignatureSize(),
184 1,
185 8,
186 MSG_SIGNATURE_SIZE)
187 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
188 (dataAdapter->getKeyDiversifier().size() >= 1 &&
189 dataAdapter->getKeyDiversifier().size() <= 8),
190 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
191
192 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
193 mSamCommands.push_back(std::make_shared<CmdSamDataCipher>(mSam, dataAdapter, nullptr));
194
195 return *this;
196
197 } catch (const std::exception& e) {
198
199 (void)e;
200 }
201
202 try {
203
204 /* Traceable signature */
205 auto dataAdapter =
206 any_cast<std::shared_ptr<TraceableSignatureComputationDataAdapter>>(data);
207
208 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
209 .isInRange(dataAdapter->getData().size(),
210 1,
211 dataAdapter->isSamTraceabilityMode() ? 206 : 208,
212 "length of data to sign")
213 .isInRange(dataAdapter->getSignatureSize(),
214 1,
215 8,
216 MSG_SIGNATURE_SIZE)
217 .isTrue(!dataAdapter->isSamTraceabilityMode() ||
218 (dataAdapter->getTraceabilityOffset() >= 0 &&
219 dataAdapter->getTraceabilityOffset() <=
220 static_cast<int>((dataAdapter->getData().size() * 8) -
221 (dataAdapter->isPartialSamSerialNumber() ?
222 7 * 8 : 8 * 8))),
223 "traceability offset is in range [0.." +
224 std::to_string(((dataAdapter->getData().size() * 8) -
225 (dataAdapter->isPartialSamSerialNumber() ? 7 * 8 : 8 * 8))) +
226 "]")
227 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
228 (dataAdapter->getKeyDiversifier().size() >= 1 &&
229 dataAdapter->getKeyDiversifier().size() <= 8),
230 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
231
232 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
233 mSamCommands.push_back(
234 std::make_shared<CmdSamPsoComputeSignature>(mSam, dataAdapter));
235
236 return *this;
237
238 } catch (const std::exception& e) {
239
240 (void)e;
241 }
242
243 throw IllegalArgumentException("The provided data must be an instance of " \
244 "'BasicSignatureComputationDataAdapter' or " \
245 "'TraceableSignatureComputationDataAdapter'");
246 }
247
253 SamTransactionManager& prepareVerifySignature(const any data) override
254 {
255 /* C++: careful, code is a little bit different from Java because of any_cast flow */
256 try {
257
258 /* Basic signature */
259 auto dataAdapter =
260 any_cast<std::shared_ptr<BasicSignatureVerificationDataAdapter>>(data);
261
262 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
263 .isInRange(dataAdapter->getData().size(),
264 1,
265 208,
266 "length of signed data to verify")
267 .isTrue(dataAdapter->getData().size() % 8 == 0,
268 "length of data to verify is a multiple of 8")
269 .isInRange(dataAdapter->getSignature().size(),
270 1,
271 8,
272 MSG_SIGNATURE_SIZE)
273 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
274 (dataAdapter->getKeyDiversifier().size() >= 1 &&
275 dataAdapter->getKeyDiversifier().size() <= 8),
276 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
277
278 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
279 mSamCommands.push_back(
280 std::make_shared<CmdSamDataCipher>(mSam, nullptr, dataAdapter));
281
282 return *this;
283
284 } catch (const std::exception& e) {
285
286 (void)e;
287 }
288
289 try {
290
291 /* Traceable signature */
292 auto dataAdapter =
293 any_cast<std::shared_ptr<TraceableSignatureVerificationDataAdapter>>(data);
294
295 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
296 .isInRange(dataAdapter->getData().size(),
297 1,
298 dataAdapter->isSamTraceabilityMode() ? 206 : 208,
299 "length of signed data to verify")
300 .isInRange(dataAdapter->getSignature().size(),
301 1,
302 8,
303 MSG_SIGNATURE_SIZE)
304 .isTrue(!dataAdapter->isSamTraceabilityMode() ||
305 (dataAdapter->getTraceabilityOffset() >= 0 &&
306 dataAdapter->getTraceabilityOffset() <=
307 static_cast<int>((dataAdapter->getData().size() * 8) -
308 (dataAdapter->isPartialSamSerialNumber() ?
309 7 * 8 : 8 * 8))),
310 "traceability offset is in range [0.." +
311 std::to_string((dataAdapter->getData().size() * 8) -
312 (dataAdapter->isPartialSamSerialNumber() ? 7 * 8 : 8 * 8)) +
313 "]")
314 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
315 (dataAdapter->getKeyDiversifier().size() >= 1 &&
316 dataAdapter->getKeyDiversifier().size() <= 8),
317 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
318
319 /* Check SAM revocation status if requested. */
320 if (dataAdapter->isSamRevocationStatusVerificationRequested()) {
321
322 Assert::getInstance().notNull(mSecuritySetting, "security settings")
323 .notNull(mSecuritySetting->getSamRevocationServiceSpi(),
324 "SAM revocation service");
325
326 /* Extract the SAM serial number and the counter value from the data. */
327 const std::vector<uint8_t> samSerialNumber =
328 ByteArrayUtil::extractBytes(dataAdapter->getData(),
329 dataAdapter->getTraceabilityOffset(),
330 dataAdapter->isPartialSamSerialNumber() ? 3 : 4);
331
332 const int samCounterValue =
333 ByteArrayUtil::extractInt(
334 ByteArrayUtil::extractBytes(dataAdapter->getData(),
335 dataAdapter->getTraceabilityOffset() +
336 (dataAdapter->isPartialSamSerialNumber() ?
337 3 * 8 : 4 * 8),
338 3),
339 0,
340 3,
341 false);
342
343 /* Is SAM revoked ? */
344 if (mSecuritySetting->getSamRevocationServiceSpi()
345 ->isSamRevoked(samSerialNumber, samCounterValue)) {
346
347 throw SamRevokedException(
348 StringUtils::format("SAM with serial number '%s' and counter value '%d' " \
349 "is revoked.",
350 HexUtil::toHex(samSerialNumber).c_str(),
351 samCounterValue));
352 }
353 }
354
355 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
356 mSamCommands.push_back(
357 std::make_shared<CmdSamPsoVerifySignature>(mSam, dataAdapter));
358
359 return *this;
360
361 } catch (const std::bad_cast& e) {
362
363 /* C++: Fall through... */
364 (void)e;
365
366 } catch (const Exception& e) {
367
368 /* C++: rethrow since we are in a try/catch block */
369 (void)e;
370 throw;
371 }
372
373 throw IllegalArgumentException("The provided data must be an instance of " \
374 "'CommonSignatureVerificationDataAdapter'");
375 }
376
382 SamTransactionManager& processCommands() override
383 {
384 if (mSamCommands.empty()) {
385
386 return *this;
387 }
388
389 try {
390
391 /* Get the list of C-APDU to transmit */
392 const std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests =
393 getApduRequests(mSamCommands);
394
395 /* Wrap the list of C-APDUs into a card request */
396 auto cardRequest = std::make_shared<CardRequestAdapter>(apduRequests, true);
397
398 /* Transmit the commands to the SAM */
399 const std::shared_ptr<CardResponseApi> cardResponse = transmitCardRequest(cardRequest);
400
401 /* Retrieve the list of R-APDUs */
402 const std::vector<std::shared_ptr<ApduResponseApi>>& apduResponses =
403 cardResponse->getApduResponses();
404
405 /*
406 * If there are more responses than requests, then we are unable to fill the card image.
407 * In this case we stop processing immediately because it may be a case of fraud, and we
408 * throw an exception.
409 */
410 if (apduResponses.size() > apduRequests.size()) {
411
412 throw InconsistentDataException("The number of SAM commands/responses does not " \
413 "match: nb commands = " +
414 std::to_string(apduRequests.size()) +
415 ", nb responses = " +
416 std::to_string(apduResponses.size()) +
418 }
419
420 /*
421 * We go through all the responses (and not the requests) because there may be fewer in
422 * the case of an error that occurred in strict mode. In this case the last response will
423 * raise an exception.
424 */
425 for (int i = 0; i < static_cast<int>(apduResponses.size()); i++) {
426
427 try {
428
429 mSamCommands[i]->parseApduResponse(apduResponses[i]);
430
431 } catch (const Exception& ex) {
432
433 /*
434 * C++: for some reason, it doesn't work if trying to catch directly the
435 * CalypsoSamCommandException exception.
436 */
438 dynamic_cast<const CalypsoSamCommandException&>(ex);
439
440 try {
441
442 const CalypsoSamCommand& commandRef =
443 std::dynamic_pointer_cast<AbstractSamCommand>(mSamCommands[i])
444 ->getCommandRef();
445
446 if (commandRef == CalypsoSamCommand::DIGEST_AUTHENTICATE) {
447
448 /* C++: cast made outside the if/else condition, will throw if false */
449 (void)static_cast<const CalypsoSamSecurityDataException&>(e);
450 throw InvalidCardSignatureException(
451 "Invalid card signature.",
452 std::make_shared<CalypsoSamCommandException>(e));
453
454 } else if (commandRef == CalypsoSamCommand::PSO_VERIFY_SIGNATURE ||
455 commandRef == CalypsoSamCommand::DATA_CIPHER) {
456
457 /* C++: cast made outside the if/else condition, will throw if false */
458 (void)static_cast<const CalypsoSamSecurityDataException&>(e);
459 throw InvalidSignatureException(
460 "Invalid signature.",
461 std::make_shared<CalypsoSamSecurityDataException>(
462 e.getMessage(),
463 dynamic_cast<const CalypsoSamCommand&>(e.getCommand()),
464 e.getStatusWord()));
465
466 } else if (commandRef == CalypsoSamCommand::SV_CHECK) {
467
468 /* C++: cast made outside the if/else condition, will throw if false */
469 (void)static_cast<const CalypsoSamSecurityDataException&>(e);
470 throw InvalidCardSignatureException(
471 "Invalid SV card signature.",
472 std::make_shared<CalypsoSamSecurityDataException>(
473 e.getMessage(),
474 dynamic_cast<const CalypsoSamCommand&>(e.getCommand()),
475 e.getStatusWord()));
476 }
477
478 } catch (const std::bad_cast& e) {
479
480 /* C++: Fall through... */
481 (void)e;
482
483 } catch (const Exception& e) {
484
485 /* C++: need to rethrow as we are in a try/catch block */
486 (void)e;
487 throw;
488 }
489
490 throw UnexpectedCommandStatusException(
492 "while processing responses to SAM commands: " +
493 e.getCommand().getName() +
495 std::make_shared<CalypsoSamCommandException>(e));
496 }
497 }
498
499 /*
500 * Finally, if no error has occurred and there are fewer responses than requests, then we
501 * throw an exception.
502 */
503 if (apduResponses.size() < apduRequests.size()) {
504
505 throw InconsistentDataException(
506 "The number of SAM commands/responses does not match:" \
507 " nb commands = " + std::to_string(apduRequests.size()) +
508 ", nb responses = " + std::to_string(apduResponses.size()) +
510 }
511
512 } catch (const Exception& e) {
513
514 /* C++: need to rethrow as we are in a try/catch block */
515 (void)e;
516
517 /* Reset the list of commands (finally) */
518 mSamCommands.clear();
519
520 throw;
521 }
522
523 /* Reset the list of commands (finally) */
524 mSamCommands.clear();
525
526 return *this;
527 }
528
529protected:
539 virtual std::vector<std::shared_ptr<AbstractApduCommand>>& getSamCommands()
540 {
541 return mSamCommands;
542 }
543
552 void prepareSelectDiversifierIfNeeded(const std::vector<uint8_t>& specificKeyDiversifier)
553 {
554 if (!specificKeyDiversifier.empty()) {
555 if (!Arrays::equals(specificKeyDiversifier, mCurrentKeyDiversifier)) {
556 mCurrentKeyDiversifier = specificKeyDiversifier;
557 prepareSelectDiversifier();
558 }
559 } else {
561 }
562 }
563
572 {
573 if (!Arrays::equals(mCurrentKeyDiversifier, mDefaultKeyDiversifier)) {
574 mCurrentKeyDiversifier = mDefaultKeyDiversifier;
575 prepareSelectDiversifier();
576 }
577 }
578
579 // /**
580 // * {@inheritDoc}
581 // *
582 // * @since 2.2.3
583 // */
584 // SamTransactionManager& prepareReadEventCounter(const int eventCounterNumber) override
585 // {
586 // throw UnsupportedOperationException("prepareReadEventCounter");
587 // }
588
589 // /**
590 // * {@inheritDoc}
591 // *
592 // * @since 2.2.3
593 // */
594 // SamTransactionManager& prepareReadEventCounters(const int fromEventCounterNumber,
595 // const int toEventCounterNumber) override
596 // {
597 // throw UnsupportedOperationException("prepareReadEventCounters");
598 // }
599
600 // /**
601 // * {@inheritDoc}
602 // *
603 // * @since 2.2.3
604 // */
605 // SamTransactionManager& prepareReadEventCeiling(const int eventCeilingNumber) override
606 // {
607 // throw UnsupportedOperationException("prepareReadEventCeiling");
608 // }
609
610 // /**
611 // * {@inheritDoc}
612 // *
613 // * @since 2.2.3
614 // */
615 // SamTransactionManager& prepareReadEventCeilings(const int fromEventCeilingNumber,
616 // const int toEventCeilingNumber) override
617 // {
618 // throw UnsupportedOperationException("prepareReadEventCeilings");
619 // }
620
621 // /**
622 // * {@inheritDoc}
623 // *
624 // * @since 2.2.3
625 // */
626 // SamTransactionManager& prepareWriteEventCeiling(const int eventCeilingNumber,
627 // const int newValue) override
628 // {
629 // throw UnsupportedOperationException("prepareWriteEventCeiling");
630 // }
631
632 // /**
633 // * {@inheritDoc}
634 // *
635 // * @since 2.2.3
636 // */
637 // SamTransactionManager& prepareWriteEventCeilings(const int fromEventCeilingNumber,
638 // const std::vector<int>& newValues) override
639 // {
640 // throw UnsupportedOperationException("prepareWriteEventCeilings");
641 // }
642
643private:
647 const std::unique_ptr<Logger> mLogger =
648 LoggerFactory::getLogger(typeid(CommonSamTransactionManagerAdapter));
649
650 const std::string MSG_INPUT_OUTPUT_DATA = "input/output data";
651 const std::string MSG_SIGNATURE_SIZE = "signature size";
652 const std::string MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8 =
653 "key diversifier size is in range [1..8]";
654
655 /* Final fields */
656 const std::shared_ptr<ProxyReaderApi> mSamReader;
657 const std::shared_ptr<CalypsoSamAdapter> mSam;
658 const std::shared_ptr<CommonSecuritySettingAdapter<CommonSecuritySetting>> mSecuritySetting;
659
660 /*
661 * C++: use AbstractApduCommand instead of AbstractSamCommand for vector vs. polymorphism
662 * constaints.
663 */
664 std::vector<std::shared_ptr<AbstractApduCommand>> mSamCommands;
665 const std::vector<uint8_t> mDefaultKeyDiversifier;
666
667 /* Dynamic fields */
668 std::vector<uint8_t> mCurrentKeyDiversifier;
669
677 virtual std::shared_ptr<CardResponseApi> transmitCardRequest(
678 const std::shared_ptr<CardRequestSpi> cardRequest)
679 {
680 std::shared_ptr<CardResponseApi> cardResponse = nullptr;
681
682 try {
683 cardResponse = mSamReader->transmitCardRequest(cardRequest, ChannelControl::KEEP_OPEN);
684
685 } catch (const ReaderBrokenCommunicationException& e) {
686 saveTransactionAuditData(cardRequest, e.getCardResponse());
687 throw ReaderIOException(MSG_SAM_READER_COMMUNICATION_ERROR +
690 std::make_shared<ReaderBrokenCommunicationException>(e));
691
692 } catch (const CardBrokenCommunicationException& e) {
693 saveTransactionAuditData(cardRequest, e.getCardResponse());
694 throw SamIOException(MSG_SAM_COMMUNICATION_ERROR +
697 std::make_shared<CardBrokenCommunicationException>(e));
698
699 } catch (const UnexpectedStatusWordException& e) {
700 mLogger->debug("A SAM command has failed: %\n", e.getMessage());
701 cardResponse = e.getCardResponse();
702 }
703
704 saveTransactionAuditData(cardRequest, cardResponse);
705
706 return cardResponse;
707 }
708
715 virtual void prepareSelectDiversifier()
716 {
717 mSamCommands.push_back(std::make_shared<CmdSamSelectDiversifier>(mSam,
718 mCurrentKeyDiversifier));
719 }
720};
721
722}
723}
724}
static const CalypsoSamCommand PSO_VERIFY_SIGNATURE
static const CalypsoSamCommand SV_CHECK
static const CalypsoSamCommand DIGEST_AUTHENTICATE
static const CalypsoSamCommand DATA_CIPHER
virtual const std::string & getName() const =0
const std::shared_ptr< CalypsoSam > getCalypsoSam() const final
const std::shared_ptr< CardReader > getSamReader() const final
CommonSamTransactionManagerAdapter(const std::shared_ptr< SmartCard > targetSmartCard, const std::shared_ptr< CommonSecuritySettingAdapter< T > > securitySetting, const std::vector< uint8_t > &defaultKeyDiversifier, const std::vector< std::vector< uint8_t > > &transactionAuditData)
virtual std::vector< std::shared_ptr< AbstractApduCommand > > & getSamCommands()
SamTransactionManager & prepareComputeSignature(const any data) override
const std::vector< std::vector< uint8_t > > & getTransactionAuditData() const final
CommonSamTransactionManagerAdapter(const std::shared_ptr< ProxyReaderApi > samReader, const std::shared_ptr< CalypsoSamAdapter > sam, const std::shared_ptr< SamSecuritySettingAdapter > securitySetting)
SamTransactionManager & prepareVerifySignature(const any data) override
void prepareSelectDiversifierIfNeeded(const std::vector< uint8_t > &specificKeyDiversifier)
virtual void saveTransactionAuditData(const std::shared_ptr< CardRequestSpi > cardRequest, const std::shared_ptr< CardResponseApi > cardResponse)
const std::vector< std::shared_ptr< ApduRequestSpi > > getApduRequests(const std::vector< std::shared_ptr< AbstractApduCommand > > &commands)
const std::vector< std::vector< uint8_t > > & getTransactionAuditData() const override