Keyple Card Calypso C++ Library 2.2.2
Reference Terminal Reader API for C++
CommonSamTransactionManagerAdapter.h
Go to the documentation of this file.
1/**************************************************************************************************
2 * Copyright (c) 2022 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
60namespace keyple {
61namespace card {
62namespace calypso {
63
64using namespace calypsonet::terminal::card;
65using namespace keyple::core::util::cpp;
66
73template <typename T>
75: public CommonTransactionManagerAdapter<SamTransactionManager,
76 SamSecuritySetting,
77 SamSecuritySettingAdapter>,
78 public SamTransactionManager {
79public:
91 const std::shared_ptr<ProxyReaderApi> samReader,
92 const std::shared_ptr<CalypsoSamAdapter> sam,
93 const std::shared_ptr<SamSecuritySettingAdapter> securitySetting)
95 sam,
96 std::reinterpret_pointer_cast<CommonSecuritySettingAdapter<SamSecuritySettingAdapter>>(
97 securitySetting),
98 std::vector<std::vector<uint8_t>>()),
99 mSamReader(samReader),
100 mSam(sam),
101 mSecuritySetting(
102 std::reinterpret_pointer_cast<CommonSecuritySettingAdapter<SamSecuritySettingAdapter>>(
103 securitySetting)),
104 mDefaultKeyDiversifier(sam->getSerialNumber()) {}
105
119 const std::shared_ptr<SmartCard> targetSmartCard,
120 const std::shared_ptr<CommonSecuritySettingAdapter<T>> securitySetting,
121 const std::vector<uint8_t>& defaultKeyDiversifier,
122 const std::vector<std::vector<uint8_t>>& transactionAuditData)
124 targetSmartCard,
125 std::reinterpret_pointer_cast<CommonSecuritySettingAdapter<SamSecuritySettingAdapter>>(
126 securitySetting),
127 transactionAuditData),
128 mSamReader(securitySetting->getControlSamReader()),
129 mSam(securitySetting->getControlSam()),
130 mSecuritySetting(securitySetting),
131 mDefaultKeyDiversifier(defaultKeyDiversifier) {}
132
139 const std::vector<std::vector<uint8_t>>& getTransactionAuditData() const final
140 {
142 }
143
149 const std::shared_ptr<CardReader> getSamReader() const final
150 {
151 return std::dynamic_pointer_cast<CardReader>(mSamReader);
152 }
153
159 const std::shared_ptr<CalypsoSam> getCalypsoSam() const final
160 {
161 return mSam;
162 }
163
169 SamTransactionManager& prepareComputeSignature(const any data) override
170 {
171 /* C++: careful, code is a little bit different from Java because of any_cast flow */
172 try {
173 /* Basic signature */
174 auto dataAdapter =
175 any_cast<std::shared_ptr<BasicSignatureComputationDataAdapter>>(data);
176
177 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
178 .isInRange(dataAdapter->getData().size(),
179 1,
180 208,
181 "length of data to sign")
182 .isTrue(dataAdapter->getData().size() % 8 == 0,
183 "length of data to sign is a multiple of 8")
184 .isInRange(dataAdapter->getSignatureSize(),
185 1,
186 8,
187 MSG_SIGNATURE_SIZE)
188 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
189 (dataAdapter->getKeyDiversifier().size() >= 1 &&
190 dataAdapter->getKeyDiversifier().size() <= 8),
191 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
192
193 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
194 mSamCommands.push_back(std::make_shared<CmdSamDataCipher>(mSam->getProductType(),
195 dataAdapter,
196 nullptr));
197 return *this;
198
199 } catch (const std::exception& e) {
200 (void)e;
201 }
202
203 try {
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(std::make_shared<CmdSamPsoComputeSignature>(
234 mSam->getProductType(),
235 dataAdapter));
236
237 return *this;
238
239 } catch (const std::exception& e) {
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 /* Basic signature */
258 auto dataAdapter =
259 any_cast<std::shared_ptr<BasicSignatureVerificationDataAdapter>>(data);
260
261 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
262 .isInRange(dataAdapter->getData().size(),
263 1,
264 208,
265 "length of signed data to verify")
266 .isTrue(dataAdapter->getData().size() % 8 == 0,
267 "length of data to verify is a multiple of 8")
268 .isInRange(dataAdapter->getSignature().size(),
269 1,
270 8,
271 MSG_SIGNATURE_SIZE)
272 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
273 (dataAdapter->getKeyDiversifier().size() >= 1 &&
274 dataAdapter->getKeyDiversifier().size() <= 8),
275 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
276
277 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
278 mSamCommands.push_back(std::make_shared<CmdSamDataCipher>(mSam->getProductType(),
279 nullptr,
280 dataAdapter));
281
282 return *this;
283
284 } catch (const std::exception& e) {
285 (void)e;
286 }
287
288 try {
289 /* Traceable signature */
290 auto dataAdapter =
291 any_cast<std::shared_ptr<TraceableSignatureVerificationDataAdapter>>(data);
292
293 Assert::getInstance().notNull(dataAdapter, MSG_INPUT_OUTPUT_DATA)
294 .isInRange(dataAdapter->getData().size(),
295 1,
296 dataAdapter->isSamTraceabilityMode() ? 206 : 208,
297 "length of signed data to verify")
298 .isInRange(dataAdapter->getSignature().size(),
299 1,
300 8,
301 MSG_SIGNATURE_SIZE)
302 .isTrue(!dataAdapter->isSamTraceabilityMode() ||
303 (dataAdapter->getTraceabilityOffset() >= 0 &&
304 dataAdapter->getTraceabilityOffset() <=
305 static_cast<int>((dataAdapter->getData().size() * 8) -
306 (dataAdapter->isPartialSamSerialNumber() ?
307 7 * 8 : 8 * 8))),
308 "traceability offset is in range [0.." +
309 std::to_string((dataAdapter->getData().size() * 8) -
310 (dataAdapter->isPartialSamSerialNumber() ? 7 * 8 : 8 * 8)) +
311 "]")
312 .isTrue(dataAdapter->isKeyDiversifierSet() == false ||
313 (dataAdapter->getKeyDiversifier().size() >= 1 &&
314 dataAdapter->getKeyDiversifier().size() <= 8),
315 MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8);
316
317 /* Check SAM revocation status if requested. */
318 if (dataAdapter->isSamRevocationStatusVerificationRequested()) {
319 Assert::getInstance().notNull(mSecuritySetting, "security settings")
320 .notNull(mSecuritySetting->getSamRevocationServiceSpi(),
321 "SAM revocation service");
322
323 /* Extract the SAM serial number and the counter value from the data. */
324 const std::vector<uint8_t> samSerialNumber =
325 ByteArrayUtil::extractBytes(dataAdapter->getData(),
326 dataAdapter->getTraceabilityOffset(),
327 dataAdapter->isPartialSamSerialNumber() ? 3 : 4);
328
329 const int samCounterValue =
330 ByteArrayUtil::extractInt(
331 ByteArrayUtil::extractBytes(dataAdapter->getData(),
332 dataAdapter->getTraceabilityOffset() +
333 (dataAdapter->isPartialSamSerialNumber() ?
334 3 * 8 : 4 * 8),
335 3),
336 0,
337 3,
338 false);
339
340 /* Is SAM revoked ? */
341 if (mSecuritySetting->getSamRevocationServiceSpi()
342 ->isSamRevoked(samSerialNumber, samCounterValue)) {
343 throw SamRevokedException(
344 StringUtils::format("SAM with serial number '%s' and counter value '%d' " \
345 "is revoked.",
346 HexUtil::toHex(samSerialNumber).c_str(),
347 samCounterValue));
348 }
349 }
350
351 prepareSelectDiversifierIfNeeded(dataAdapter->getKeyDiversifier());
352 mSamCommands.push_back(
353 std::make_shared<CmdSamPsoVerifySignature>(mSam->getProductType(), dataAdapter));
354
355 return *this;
356
357 } catch (const std::bad_cast& e) {
358 /* C++: Fall through... */
359 (void)e;
360
361 } catch (const Exception& e) {
362 /* C++: rethrow since we are in a try/catch block */
363 (void)e;
364 throw;
365 }
366
367 throw IllegalArgumentException("The provided data must be an instance of " \
368 "'CommonSignatureVerificationDataAdapter'");
369 }
370
376 SamTransactionManager& processCommands() override
377 {
378 if (mSamCommands.empty()) {
379 return *this;
380 }
381
382 try {
383 /* Get the list of C-APDU to transmit */
384 const std::vector<std::shared_ptr<ApduRequestSpi>> apduRequests =
385 getApduRequests(mSamCommands);
386
387 /* Wrap the list of C-APDUs into a card request */
388 auto cardRequest = std::make_shared<CardRequestAdapter>(apduRequests, true);
389
390 /* Transmit the commands to the SAM */
391 const std::shared_ptr<CardResponseApi> cardResponse = transmitCardRequest(cardRequest);
392
393 /* Retrieve the list of R-APDUs */
394 const std::vector<std::shared_ptr<ApduResponseApi>>& apduResponses =
395 cardResponse->getApduResponses();
396
397
398 std::cout << "apduResponses size: " << apduResponses.size() << ", "
399 << "apduRequests size: " << apduRequests.size()
400 << std::endl;
401
402 /*
403 * If there are more responses than requests, then we are unable to fill the card image.
404 * In this case we stop processing immediately because it may be a case of fraud, and we
405 * throw an exception.
406 */
407 if (apduResponses.size() > apduRequests.size()) {
408 throw InconsistentDataException("The number of SAM commands/responses does not " \
409 "match: nb commands = " +
410 std::to_string(apduRequests.size()) +
411 ", nb responses = " +
412 std::to_string(apduResponses.size()) +
414 }
415
416 /*
417 * We go through all the responses (and not the requests) because there may be fewer in
418 * the case of an error that occurred in strict mode. In this case the last response will
419 * raise an exception.
420 */
421 for (int i = 0; i < static_cast<int>(apduResponses.size()); i++) {
422 try {
423 mSamCommands[i]->setApduResponse(apduResponses[i]).checkStatus();
424
425 } catch (const Exception& ex) {
426 /*
427 * C++: for some reason, it doesn't work if trying to catch directly the
428 * CalypsoSamCommandException exception.
429 */
431 dynamic_cast<const CalypsoSamCommandException&>(ex);
432
433 try {
434 const CalypsoSamCommand& commandRef =
435 std::dynamic_pointer_cast<AbstractSamCommand>(mSamCommands[i])
436 ->getCommandRef();
437
438 if (commandRef == CalypsoSamCommand::DIGEST_AUTHENTICATE) {
439 /* C++: cast made outside the if/else condition, will throw if false */
440 (void)static_cast<const CalypsoSamSecurityDataException&>(e);
441 throw InvalidCardSignatureException(
442 "Invalid card signature.",
443 std::make_shared<CalypsoSamCommandException>(e));
444
445 } else if (commandRef == CalypsoSamCommand::PSO_VERIFY_SIGNATURE ||
446 commandRef == CalypsoSamCommand::DATA_CIPHER) {
447 /* C++: cast made outside the if/else condition, will throw if false */
448 (void)static_cast<const CalypsoSamSecurityDataException&>(e);
449 throw InvalidSignatureException(
450 "Invalid signature.",
451 std::make_shared<CalypsoSamSecurityDataException>(
452 e.getMessage(),
453 dynamic_cast<const CalypsoSamCommand&>(e.getCommand()),
454 e.getStatusWord()));
455
456 } else if (commandRef == CalypsoSamCommand::SV_CHECK) {
457 /* C++: cast made outside the if/else condition, will throw if false */
458 (void)static_cast<const CalypsoSamSecurityDataException&>(e);
459 throw InvalidCardSignatureException(
460 "Invalid SV card signature.",
461 std::make_shared<CalypsoSamSecurityDataException>(
462 e.getMessage(),
463 dynamic_cast<const CalypsoSamCommand&>(e.getCommand()),
464 e.getStatusWord()));
465 }
466
467 } catch (const std::bad_cast& e) {
468 /* C++: Fall through... */
469 (void)e;
470
471 } catch (const Exception& e) {
472 /* C++: need to rethrow as we are in a try/catch block */
473 (void)e;
474 throw;
475 }
476
477 throw UnexpectedCommandStatusException(
479 "while processing responses to SAM commands: " +
480 e.getCommand().getName() +
482 std::make_shared<CalypsoSamCommandException>(e));
483 }
484 }
485
486 /*
487 * Finally, if no error has occurred and there are fewer responses than requests, then we
488 * throw an exception.
489 */
490 if (apduResponses.size() < apduRequests.size()) {
491 throw InconsistentDataException(
492 "The number of SAM commands/responses does not match:" \
493 " nb commands = " + std::to_string(apduRequests.size()) +
494 ", nb responses = " + std::to_string(apduResponses.size()) +
496 }
497
498 } catch (const Exception& e) {
499 /* C++: need to rethrow as we are in a try/catch block */
500 (void)e;
501
502 /* Reset the list of commands (finally) */
503 mSamCommands.clear();
504
505 throw;
506 }
507
508 /* Reset the list of commands (finally) */
509 mSamCommands.clear();
510
511 return *this;
512 }
513
514protected:
524 virtual std::vector<std::shared_ptr<AbstractApduCommand>>& getSamCommands()
525 {
526 return mSamCommands;
527 }
528
537 void prepareSelectDiversifierIfNeeded(const std::vector<uint8_t>& specificKeyDiversifier)
538 {
539 if (!specificKeyDiversifier.empty()) {
540 if (!Arrays::equals(specificKeyDiversifier, mCurrentKeyDiversifier)) {
541 mCurrentKeyDiversifier = specificKeyDiversifier;
542 prepareSelectDiversifier();
543 }
544 } else {
546 }
547 }
548
557 {
558 if (!Arrays::equals(mCurrentKeyDiversifier, mDefaultKeyDiversifier)) {
559 mCurrentKeyDiversifier = mDefaultKeyDiversifier;
560 prepareSelectDiversifier();
561 }
562 }
563
564private:
568 const std::unique_ptr<Logger> mLogger =
569 LoggerFactory::getLogger(typeid(CommonSamTransactionManagerAdapter));
570
571 const std::string MSG_INPUT_OUTPUT_DATA = "input/output data";
572 const std::string MSG_SIGNATURE_SIZE = "signature size";
573 const std::string MSG_KEY_DIVERSIFIER_SIZE_IS_IN_RANGE_1_8 =
574 "key diversifier size is in range [1..8]";
575
576 /* Final fields */
577 const std::shared_ptr<ProxyReaderApi> mSamReader;
578 const std::shared_ptr<CalypsoSamAdapter> mSam;
579 const std::shared_ptr<CommonSecuritySettingAdapter<T>> mSecuritySetting;
580
581 /*
582 * C++: use AbstractApduCommand instead of AbstractSamCommand for vector vs. polymorphism
583 * constaints.
584 */
585 std::vector<std::shared_ptr<AbstractApduCommand>> mSamCommands;
586 const std::vector<uint8_t> mDefaultKeyDiversifier;
587
588 /* Dynamic fields */
589 std::vector<uint8_t> mCurrentKeyDiversifier;
590
598 virtual std::shared_ptr<CardResponseApi> transmitCardRequest(
599 const std::shared_ptr<CardRequestSpi> cardRequest)
600 {
601 std::shared_ptr<CardResponseApi> cardResponse = nullptr;
602
603 try {
604 cardResponse = mSamReader->transmitCardRequest(cardRequest, ChannelControl::KEEP_OPEN);
605
606 } catch (const ReaderBrokenCommunicationException& e) {
607 saveTransactionAuditData(cardRequest, e.getCardResponse());
608 throw ReaderIOException(MSG_SAM_READER_COMMUNICATION_ERROR +
611 std::make_shared<ReaderBrokenCommunicationException>(e));
612
613 } catch (const CardBrokenCommunicationException& e) {
614 saveTransactionAuditData(cardRequest, e.getCardResponse());
615 throw SamIOException(MSG_SAM_COMMUNICATION_ERROR +
618 std::make_shared<CardBrokenCommunicationException>(e));
619
620 } catch (const UnexpectedStatusWordException& e) {
621 mLogger->debug("A SAM command has failed: %\n", e.getMessage());
622 cardResponse = e.getCardResponse();
623 }
624
625 saveTransactionAuditData(cardRequest, cardResponse);
626
627 return cardResponse;
628 }
629
636 virtual void prepareSelectDiversifier()
637 {
638 mSamCommands.push_back(std::make_shared<CmdSamSelectDiversifier>(mSam->getProductType(),
639 mCurrentKeyDiversifier));
640 }
641};
642
643}
644}
645}
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