Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandOpenSecureSession.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/CommandOpenSecureSession.hpp"
15
16#include <map>
17#include <memory>
18#include <sstream>
19#include <string>
20#include <utility>
21#include <vector>
22
23#include "keyple/card/calypso/CalypsoCardAdapter.hpp"
24#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
25#include "keyple/card/calypso/CardDataAccessException.hpp"
26#include "keyple/card/calypso/CardIllegalParameterException.hpp"
27#include "keyple/card/calypso/CardSecurityContextException.hpp"
28#include "keyple/card/calypso/CardTerminatedException.hpp"
29#include "keyple/card/calypso/CardUnexpectedResponseLengthException.hpp"
30#include "keyple/core/util/ApduUtil.hpp"
31#include "keyple/core/util/ByteArrayUtil.hpp"
32#include "keyple/core/util/HexUtil.hpp"
33#include "keyple/core/util/cpp/Arrays.hpp"
34#include "keyple/core/util/cpp/System.hpp"
35#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
36#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
37#include "keypop/calypso/card/transaction/CryptoIOException.hpp"
38#include "keypop/calypso/card/transaction/UnauthorizedKeyException.hpp"
39#include "keypop/calypso/crypto/asymmetric/AsymmetricCryptoException.hpp"
40
41namespace keyple {
42namespace card {
43namespace calypso {
44
45using keyple::core::util::ApduUtil;
46using keyple::core::util::ByteArrayUtil;
47using keyple::core::util::HexUtil;
48using keyple::core::util::cpp::Arrays;
49using keyple::core::util::cpp::System;
50using keyple::core::util::cpp::exception::IllegalArgumentException;
51using keyple::core::util::cpp::exception::IllegalStateException;
52using keypop::calypso::card::transaction::CryptoIOException;
53using keypop::calypso::card::transaction::UnauthorizedKeyException;
54using keypop::calypso::crypto::asymmetric::AsymmetricCryptoException;
55
56const std::string CommandOpenSecureSession::PATTERN_1_BYTE_HEX = "%02Xh";
57
58const std::map<int, const std::shared_ptr<Command::StatusProperties>>
59 CommandOpenSecureSession::STATUS_TABLE = [] {
60 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
61 Command::STATUS_TABLE);
62
63 m.insert(
64 {{0x6700,
65 std::make_shared<StatusProperties>(
66 "Lc value not supported",
67 typeid(CardIllegalParameterException))},
68 {0x6900,
69 std::make_shared<StatusProperties>(
70 "Transaction Counter is 0", typeid(CardTerminatedException))},
71 {0x6981,
72 std::make_shared<StatusProperties>(
73 "Command forbidden (read requested and current EF is a Binary"
74 " file)",
75 typeid(CardDataAccessException))},
76 {0x6982,
77 std::make_shared<StatusProperties>(
78 "Security conditions not fulfilled (PIN code not presented, "
79 "AES key forbidding the compatibility mode, encryption "
80 "required)",
81 typeid(CardSecurityContextException))},
82 {0x6985,
83 std::make_shared<StatusProperties>(
84 "Access forbidden (Never access mode, Session already "
85 "opened)",
86 typeid(CardAccessForbiddenException))},
87 {0x6986,
88 std::make_shared<StatusProperties>(
89 "Command not allowed (read requested and no current EF)",
90 typeid(CardDataAccessException))},
91 {0x6A81,
92 std::make_shared<StatusProperties>(
93 "Wrong key index", typeid(CardIllegalParameterException))},
94 {0x6A82,
95 std::make_shared<StatusProperties>(
96 "File not found", typeid(CardDataAccessException))},
97 {0x6A83,
98 std::make_shared<StatusProperties>(
99 "Record not found (record index is above NumRec)",
100 typeid(CardDataAccessException))},
101 {0x6B00,
102 std::make_shared<StatusProperties>(
103 "P1 or P2 value not supported (key index incorrect, wrong P2,"
104 " extended mode not supported)",
105 typeid(CardIllegalParameterException))},
106 {0x61FF,
107 std::make_shared<StatusProperties>(
108 "Correct execution (ISO7816 T=0)",
109 typeid(CardIllegalParameterException))},
110 {0x6200,
111 std::make_shared<StatusProperties>(
112 "Successful execution, with warning (Pre-Open variant, secure"
113 " session not opened)",
114 typeid(CardIllegalParameterException))}});
115 return m;
116 }();
117
118CommandOpenSecureSession::CommandOpenSecureSession(
119 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
120 transactionContext,
121 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
122 WriteAccessLevel writeAccessLevel)
123: Command(
124 CardCommandRef::OPEN_SECURE_SESSION,
125 nullptr,
126 transactionContext,
127 commandContext)
128, mWriteAccessLevel(writeAccessLevel)
129, mIsExtendedModeAllowed(true)
130, mIsPreOpenModeOnSelection(true)
131, mIsPreOpenMode(false)
132, mIsReadModeConfigured(false)
133, mSfi(0)
134, mRecordNumber(0)
135, mExpectedRecordDataLength(0)
136{
137 /* with no SAM challenge */
138 createRev3(
139 static_cast<std::uint8_t>(static_cast<int>(mWriteAccessLevel) + 1), {});
140 addSubName("PRE-OPEN");
141}
142
143CommandOpenSecureSession::CommandOpenSecureSession(
144 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
145 transactionContext,
146 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
147 std::shared_ptr<SymmetricCryptoSecuritySettingAdapter>
148 symmetricCryptoSecuritySetting,
149 WriteAccessLevel writeAccessLevel,
150 bool isExtendedModeAllowed)
151: Command(
152 CardCommandRef::OPEN_SECURE_SESSION,
153 nullptr,
154 transactionContext,
155 commandContext)
156, mWriteAccessLevel(writeAccessLevel)
157, mIsExtendedModeAllowed(isExtendedModeAllowed)
158, mSymmetricCryptoSecuritySetting(symmetricCryptoSecuritySetting)
159, mIsPreOpenModeOnSelection(false)
160, mIsReadModeConfigured(false)
161, mSfi(0)
162, mRecordNumber(0)
163, mExpectedRecordDataLength(0)
164{
165 /* C++ */
166 std::shared_ptr<CalypsoCardAdapter> card = transactionContext->getCard();
167 if (card == nullptr) {
168 throw IllegalStateException("Card is required in transaction context");
169 }
170
171 mPreOpenDataOut = card->getPreOpenDataOut();
172 mIsPreOpenMode = !mPreOpenDataOut.empty();
173}
174
175CommandOpenSecureSession::CommandOpenSecureSession(
176 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
177 transactionContext,
178 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
179 const std::vector<std::uint8_t>& terminalChallenge)
180: Command(
181 CardCommandRef::OPEN_SECURE_SESSION,
182 nullptr,
183 transactionContext,
184 commandContext)
185, mWriteAccessLevel(WriteAccessLevel::UNKOWN)
186, mIsExtendedModeAllowed(true)
187, mIsPreOpenModeOnSelection(false)
188, mIsPreOpenMode(false)
189, mIsReadModeConfigured(false)
190, mSfi(0)
191, mRecordNumber(0)
192, mExpectedRecordDataLength(0)
193{
194 createRev3Pki(terminalChallenge);
195 addSubName("PKI");
196}
197
198const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
199CommandOpenSecureSession::getStatusTable() const
200{
201 return STATUS_TABLE;
202}
203
204void
205CommandOpenSecureSession::createRev3(
206 std::uint8_t keyIndex, const std::vector<std::uint8_t>& samChallenge)
207{
208 const std::uint8_t p1
209 = static_cast<std::uint8_t>((mRecordNumber * 8) + keyIndex);
210
211 std::uint8_t p2;
212 std::vector<std::uint8_t> dataIn;
213
214 if (mIsExtendedModeAllowed) {
215 p2 = static_cast<std::uint8_t>((mSfi * 8) + 2);
216 dataIn.resize(samChallenge.size() + 1);
217 System::arraycopy(samChallenge, 0, dataIn, 1, samChallenge.size());
218 } else {
219 p2 = static_cast<std::uint8_t>((mSfi * 8) + 1);
220 dataIn = samChallenge;
221 }
222
223 /*
224 * Case 4: this command contains incoming and outgoing data. We define le =
225 * 0, the actual length will be processed by the lower layers.
226 */
227 setApduRequest(
228 std::unique_ptr<DtoAdapters::ApduRequestAdapter>(
229 new DtoAdapters::ApduRequestAdapter(
230 ApduUtil::build(
231 CalypsoCardClass::ISO.getValue(),
232 CardCommandRef::OPEN_SECURE_SESSION.getInstructionByte(),
233 p1,
234 p2,
235 dataIn,
236 0))));
237
238 addSubName(
239 "Key index: " + std::to_string(keyIndex) + ", "
240 + "SFI: " + HexUtil::toHex(static_cast<std::uint8_t>(mSfi)) + "h, "
241 + "Rec: " + std::to_string(mRecordNumber));
242}
243
244void
245CommandOpenSecureSession::createRev24(
246 std::uint8_t keyIndex, const std::vector<std::uint8_t>& samChallenge)
247{
248 const std::uint8_t p1
249 = static_cast<std::uint8_t>(0x80 + (mRecordNumber * 8) + keyIndex);
250 buildLegacyApduRequest(keyIndex, samChallenge, mSfi, mRecordNumber, p1);
251}
252
253void
254CommandOpenSecureSession::createRev10(
255 std::uint8_t keyIndex, const std::vector<std::uint8_t>& samChallenge)
256{
257 const std::uint8_t p1
258 = static_cast<std::uint8_t>((mRecordNumber * 8) + keyIndex);
259 buildLegacyApduRequest(keyIndex, samChallenge, mSfi, mRecordNumber, p1);
260}
261
262void
263CommandOpenSecureSession::createRev3Pki(
264 const std::vector<std::uint8_t>& terminalChallenge)
265{
266 const std::uint8_t p1 = 0x00;
267 const std::uint8_t p2 = 0x03;
268
269 std::vector<std::uint8_t> dataIn(terminalChallenge.size() + 1);
270 System::arraycopy(
271 terminalChallenge, 0, dataIn, 1, terminalChallenge.size());
272
273 /*
274 * Case 4: this command contains incoming and outgoing data. We define le =
275 * 0, the actual length will be processed by the lower layers.
276 */
277 setApduRequest(
278 std::unique_ptr<DtoAdapters::ApduRequestAdapter>(
279 new DtoAdapters::ApduRequestAdapter(
280 ApduUtil::build(
281 CalypsoCardClass::ISO.getValue(),
282 CardCommandRef::OPEN_SECURE_SESSION.getInstructionByte(),
283 p1,
284 p2,
285 dataIn,
286 0))));
287}
288
289void
290CommandOpenSecureSession::buildLegacyApduRequest(
291 std::uint8_t keyIndex,
292 const std::vector<std::uint8_t>& samChallenge,
293 int sfi,
294 int recordNumber,
295 std::uint8_t p1)
296{
297 const std::uint8_t p2 = static_cast<std::uint8_t>(sfi * 8);
298
299 /*
300 * case 4: this command contains incoming and outgoing data. We define le =
301 * 0, the actual length will be processed by the lower layers.
302 */
303 setApduRequest(
304 std::unique_ptr<DtoAdapters::ApduRequestAdapter>(
305 new DtoAdapters::ApduRequestAdapter(
306 ApduUtil::build(
307 CalypsoCardClass::LEGACY.getValue(),
308 CardCommandRef::OPEN_SECURE_SESSION.getInstructionByte(),
309 p1,
310 p2,
311 samChallenge,
312 0))));
313
314 addSubName(
315 "Key index: " + std::to_string(keyIndex) + ", "
316 + "SFI: " + HexUtil::toHex(static_cast<std::uint8_t>(sfi)) + "h, "
317 + "Rec: " + std::to_string(recordNumber));
318}
319
320void
321CommandOpenSecureSession::configureReadMode(
322 int sfi, int recordNumber, int expectedRecordDataLength)
323{
324 if (getTransactionContext()->isPkiMode()) {
325 std::shared_ptr<DtoAdapters::ApduRequestAdapter> request
326 = getApduRequest();
327 std::vector<std::uint8_t> apdu = request->getApdu();
328
329 /* Overwrite p1 & p2 */
330 apdu[2] = static_cast<std::uint8_t>(recordNumber * 8);
331 apdu[3] = static_cast<std::uint8_t>((sfi * 8) + 3);
332
333 request->setApdu(apdu);
334
335 addSubName(
336 std::string("SFI: ")
337 + HexUtil::toHex(static_cast<std::uint8_t>(sfi))
338 + "h, Rec: " + std::to_string(recordNumber));
339 }
340
341 mSfi = sfi;
342 mRecordNumber = recordNumber;
343 mExpectedRecordDataLength = expectedRecordDataLength;
344 mIsReadModeConfigured = true;
345}
346
347bool
348CommandOpenSecureSession::isReadModeConfigured() const
349{
350 return mIsReadModeConfigured;
351}
352
353void
354CommandOpenSecureSession::finalizeRequest()
355{
356 std::vector<std::uint8_t> samChallenge;
357
358 try {
359 samChallenge = getTransactionContext()
360 ->getSymmetricCryptoCardTransactionManagerSpi()
361 ->initTerminalSecureSessionContext();
362
363 } catch (const SymmetricCryptoException& e) {
364 throw CryptoException(e.what(), e);
365
366 } catch (const SymmetricCryptoIOException& e) {
367 throw CryptoIOException(e.what(), e);
368 }
369
370 const std::uint8_t keyIndex
371 = static_cast<std::uint8_t>(static_cast<int>(mWriteAccessLevel) + 1);
372
373 switch (getTransactionContext()->getCard()->getProductType()) {
374 case CalypsoCard::ProductType::PRIME_REVISION_1:
375 createRev10(keyIndex, samChallenge);
376 break;
377 case CalypsoCard::ProductType::PRIME_REVISION_2:
378 createRev24(keyIndex, samChallenge);
379 break;
380 case CalypsoCard::ProductType::PRIME_REVISION_3:
381 case CalypsoCard::ProductType::LIGHT:
382 case CalypsoCard::ProductType::BASIC:
383 createRev3(keyIndex, samChallenge);
384 break;
385 default:
386 std::stringstream ss;
387 ss << "Unsupported ProductType: "
388 << getTransactionContext()->getCard()->getProductType();
389 throw IllegalArgumentException(ss.str());
390 }
391}
392
393bool
394CommandOpenSecureSession::isCryptoServiceRequiredToFinalizeRequest() const
395{
396 return true;
397}
398
399bool
400CommandOpenSecureSession::synchronizeCryptoServiceBeforeCardProcessing()
401{
402 if (!mIsPreOpenMode) {
403 return false;
404 }
405
406 /*
407 * In pre-open mode, we can synchronize the crypto service without having to
408 * execute the card open session command first.
409 */
410 if (!isCryptoServiceSynchronized()) {
411 try {
412 parseRev3(mPreOpenDataOut);
413
414 } catch (const CardUnexpectedResponseLengthException& e) {
415 throw IllegalStateException(
416 "Unexpected response length in pre-open mode", e);
417 }
418
419 synchronizeCryptoService(mPreOpenDataOut);
420 }
421
422 return true;
423}
424
425void
426CommandOpenSecureSession::synchronizeCryptoService(
427 const std::vector<std::uint8_t>& dataOut)
428{
429 if (getTransactionContext()->isPkiMode()) {
430 try {
431 std::shared_ptr<AsymmetricCryptoCardTransactionManagerSpi>
432 cryptoManager
433 = getTransactionContext()
434 ->getAsymmetricCryptoCardTransactionManagerSpi();
435 cryptoManager->initTerminalPkiSession(
436 getTransactionContext()->getCard()->getCardPublicKeySpi());
437 cryptoManager->updateTerminalPkiSession(
438 getApduRequest()->getApdu());
439 cryptoManager->updateTerminalPkiSession(
440 getApduResponse()->getApdu());
441
442 } catch (const AsymmetricCryptoException& e) {
443 throw CryptoException(e.what(), e);
444 }
445
446 } else {
447 std::shared_ptr<std::uint8_t> computedKvc = computeKvc();
448 std::shared_ptr<std::uint8_t> computedKif
449 = computeKif(computedKvc.get());
450 if (!mSymmetricCryptoSecuritySetting->isSessionKeyAuthorized(
451 computedKif, computedKvc)) {
452 throw UnauthorizedKeyException(
453 std::string("Unauthorized key. KIF: ")
454 + HexUtil::toHex(*computedKif)
455 + ", KVC: " + std::to_string(*computedKvc));
456 }
457
458 try {
459 getTransactionContext()
460 ->getSymmetricCryptoCardTransactionManagerSpi()
461 ->initTerminalSessionMac(dataOut, *computedKif, *computedKvc);
462
463 } catch (const SymmetricCryptoException& e) {
464 throw CryptoException(e.what(), e);
465
466 } catch (const SymmetricCryptoIOException& e) {
467 throw CryptoIOException(e.what(), e);
468 }
469 }
470
471 confirmCryptoServiceSuccessfullySynchronized();
472}
473
474void
475CommandOpenSecureSession::parseResponse(
476 std::shared_ptr<ApduResponseApi> apduResponse)
477{
478 Command::setApduResponseAndCheckStatus(apduResponse);
479 std::shared_ptr<CalypsoCardAdapter> card
480 = getTransactionContext()->getCard();
481 if (!mIsPreOpenModeOnSelection) {
482 card->backupFiles();
483 getTransactionContext()->setSecureSessionOpen(true);
484 }
485
486 /* Parse data */
487 const std::vector<std::uint8_t> dataOut = getApduResponse()->getDataOut();
488 if (getTransactionContext()->isPkiMode()) {
489 parsePki(dataOut);
490 } else {
491 switch (card->getProductType()) {
492 case CalypsoCard::ProductType::PRIME_REVISION_1:
493 parseRev10(dataOut);
494 break;
495 case CalypsoCard::ProductType::PRIME_REVISION_2:
496 parseRev24(dataOut);
497 break;
498 default:
499 parseRev3(dataOut);
500 }
501 }
502
503 /*
504 * Update Calypso card image
505 * CL-CSS-INFORAT.1
506 */
507 card->setDfRatified(mIsPreviousSessionRatified);
508
509 /* CL-CSS-INFOTCNT.1 */
510 card->setTransactionCounter(
511 ByteArrayUtil::extractInt(mChallengeTransactionCounter, 0, 3, false));
512 if (mRecordData.size() > 0) {
513 card->setContent(
514 static_cast<std::uint8_t>(mSfi),
515 static_cast<std::uint8_t>(mRecordNumber),
516 mRecordData);
517 }
518 /*
519 * If it is a pre-open variant, then we save the pre-open data into the
520 * Calypso card image.
521 */
522 if (mIsPreOpenModeOnSelection && apduResponse->getStatusWord() == 0x6200) {
523 card->setPreOpenWriteAccessLevel(mWriteAccessLevel);
524 card->setPreOpenDataOut(dataOut);
525 }
526
527 /* Synchronize crypto service */
528 if (!isCryptoServiceSynchronized()) {
529 synchronizeCryptoService(dataOut);
530 } else {
531 /*
532 * If the crypto service is already synchronized, this means you're in
533 * pre-open mode.
534 */
535 if (!Arrays::equals(dataOut, mPreOpenDataOut)) {
536 throw CardSecurityContextException(
537 "Session has been pre-opened but 'dataOut' fields do not match",
538 CardCommandRef::OPEN_SECURE_SESSION);
539 }
540 }
541}
542
543std::shared_ptr<std::uint8_t>
544CommandOpenSecureSession::computeKvc()
545{
546 if (mKvc != nullptr) {
547 return mKvc;
548 }
549
550 return mSymmetricCryptoSecuritySetting->getDefaultKvc(mWriteAccessLevel);
551}
552
553std::shared_ptr<std::uint8_t>
554CommandOpenSecureSession::computeKif(const std::uint8_t* kvc)
555{
556 /* CL-KEY-KIF.1 */
557 if ((mKif != nullptr && *mKif != 0xFF) || (kvc == nullptr)) {
558 return mKif;
559 }
560
561 /* CL-KEY-KIFUNK.1 */
562 std::shared_ptr<std::uint8_t> result
563 = mSymmetricCryptoSecuritySetting->getKif(mWriteAccessLevel, *kvc);
564 if (result == nullptr) {
565 result
566 = mSymmetricCryptoSecuritySetting->getDefaultKif(mWriteAccessLevel);
567 }
568
569 return result;
570}
571
572void
573CommandOpenSecureSession::parseRev3(
574 const std::vector<std::uint8_t>& apduResponseData)
575{
576 int offset;
577
578 /* CL-CSS-OSSRFU.1 */
579 if (mIsExtendedModeAllowed) {
580 offset = 4;
581 mIsPreviousSessionRatified = (apduResponseData[8] & 0x01) == 0x00;
582 bool manageSecureSessionAuthorized
583 = (apduResponseData[8] & 0x02) == 0x02;
584 if (!manageSecureSessionAuthorized) {
585 getTransactionContext()->getCard()->disableExtendedMode();
586 }
587
588 } else {
589 offset = 0;
590 mIsPreviousSessionRatified = (apduResponseData[4] == 0x00);
591 getTransactionContext()->getCard()->disableExtendedMode();
592 }
593
594 mChallengeTransactionCounter = Arrays::copyOfRange(apduResponseData, 0, 3);
595 mKif = std::make_shared<std::uint8_t>(apduResponseData[5 + offset]);
596 mKvc = std::make_shared<std::uint8_t>(apduResponseData[6 + offset]);
597 int dataLength = apduResponseData[7 + offset];
598 if (dataLength != static_cast<int>(apduResponseData.size() - 8 - offset)) {
599 throw CardUnexpectedResponseLengthException(
600 std::string("APDU response is not the correct length. Command: ")
601 + getCommandRef().getName()
602 + ", Expected: " + std::to_string(mExpectedRecordDataLength)
603 + ", Actual: " + std::to_string(dataLength),
604 getCommandRef());
605 }
606 mRecordData = Arrays::copyOfRange(
607 apduResponseData, 8 + offset, 8 + offset + dataLength);
608}
609
610void
611CommandOpenSecureSession::parseRev24(
612 const std::vector<std::uint8_t>& apduResponseData)
613{
614 switch (apduResponseData.size()) {
615 case 5:
616 mIsPreviousSessionRatified = true;
617 mRecordData = {};
618 break;
619 case 34:
620 checkReceivedDataLength(29);
621 mIsPreviousSessionRatified = true;
622 mRecordData = Arrays::copyOfRange(apduResponseData, 5, 34);
623 break;
624 case 7:
625 mIsPreviousSessionRatified = false;
626 mRecordData = {};
627 break;
628 case 36:
629 checkReceivedDataLength(29);
630 mIsPreviousSessionRatified = false;
631 mRecordData = Arrays::copyOfRange(apduResponseData, 7, 36);
632 break;
633 default:
634 throw CardUnexpectedResponseLengthException(
635 std::string("APDU response is not the correct length. Command: ")
636 + getCommandRef().getName() + ", Expected: 5/7/34/36, Actual: "
637 + std::to_string(apduResponseData.size()),
638 getCommandRef());
639 }
640
641 mChallengeTransactionCounter = Arrays::copyOfRange(apduResponseData, 1, 4);
642 mKif = nullptr;
643 mKvc = std::unique_ptr<std::uint8_t>(new std::uint8_t(apduResponseData[0]));
644}
645
646void
647CommandOpenSecureSession::parseRev10(
648 const std::vector<std::uint8_t>& apduResponseData)
649{
650 switch (apduResponseData.size()) {
651 case 4:
652 mIsPreviousSessionRatified = true;
653 mRecordData = {};
654 break;
655 case 33:
656 checkReceivedDataLength(29);
657 mIsPreviousSessionRatified = true;
658 mRecordData = Arrays::copyOfRange(apduResponseData, 4, 33);
659 break;
660 case 6:
661 mIsPreviousSessionRatified = false;
662 mRecordData = {};
663 break;
664 case 35:
665 checkReceivedDataLength(29);
666 mIsPreviousSessionRatified = false;
667 mRecordData = Arrays::copyOfRange(apduResponseData, 6, 35);
668 break;
669 default:
670 throw CardUnexpectedResponseLengthException(
671 "APDU response is not the correct length. Command: "
672 + getCommandRef().getName() + ", Expected: 4/6/33/35, Actual: "
673 + std::to_string(apduResponseData.size()),
674 getCommandRef());
675 }
676
677 mChallengeTransactionCounter = Arrays::copyOfRange(apduResponseData, 0, 3);
678 mKif = nullptr;
679 mKvc = nullptr;
680}
681
682void
683CommandOpenSecureSession::parsePki(
684 const std::vector<std::uint8_t>& apduResponseData)
685{
686 const int li = apduResponseData[0] & 0xFF;
687 int offset = 1 + li + 8 + 1;
688
689 mChallengeTransactionCounter
690 = Arrays::copyOfRange(apduResponseData, offset, offset + 3);
691 offset += 8;
692
693 mIsPreviousSessionRatified = (apduResponseData[offset] & 0x01) == 0x00;
694 offset += 1 + 2;
695
696 const int ld = apduResponseData[offset] & 0xFF;
697 offset += 1;
698
699 mRecordData = Arrays::copyOfRange(apduResponseData, offset, offset + ld);
700}
701
702void
703CommandOpenSecureSession::checkReceivedDataLength(int dataLength)
704{
705 if (dataLength != mExpectedRecordDataLength) {
706 throw CardUnexpectedResponseLengthException(
707 "APDU response is not the correct length. Command: "
708 + getCommandRef().getName()
709 + ", Expected: " + std::to_string(mExpectedRecordDataLength)
710 + ", Actual: " + std::to_string(dataLength),
711 getCommandRef());
712 }
713}
714
715} /* namespace calypso */
716} /* namespace card */
717} /* namespace keyple */