Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandReadBinary.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/CommandReadBinary.hpp"
15
16#include <limits>
17#include <map>
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "keyple/card/calypso/CalypsoCardAdapter.hpp"
23#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
24#include "keyple/card/calypso/CardDataAccessException.hpp"
25#include "keyple/card/calypso/CardIllegalParameterException.hpp"
26#include "keyple/card/calypso/CardSecurityContextException.hpp"
27#include "keyple/core/util/ApduUtil.hpp"
28#include "keyple/core/util/HexUtil.hpp"
29#include "keyple/core/util/cpp/Arrays.hpp"
30#include "keyple/core/util/cpp/System.hpp"
31#include "keyple/core/util/cpp/exception/IndexOutOfBoundsException.hpp"
32
33namespace keyple {
34namespace card {
35namespace calypso {
36
37using keyple::core::util::ApduUtil;
38using keyple::core::util::HexUtil;
39using keyple::core::util::cpp::Arrays;
40using keyple::core::util::cpp::System;
41using keyple::core::util::cpp::exception::IndexOutOfBoundsException;
42
43const std::map<int, const std::shared_ptr<Command::StatusProperties>>
44 CommandReadBinary::STATUS_TABLE = [] {
45 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
46 Command::STATUS_TABLE);
47
48 m.insert(
49 {{0x6981,
50 std::make_shared<StatusProperties>(
51 "Incorrect EF type: not a Binary EF",
52 typeid(CardDataAccessException))},
53 {0x6982,
54 std::make_shared<StatusProperties>(
55 "Security conditions not fulfilled (PIN code not presented, "
56 "encryption required)",
57 typeid(CardSecurityContextException))},
58 {0x6985,
59 std::make_shared<StatusProperties>(
60 "Access forbidden (Never access mode)",
61 typeid(CardAccessForbiddenException))},
62 {0x6986,
63 std::make_shared<StatusProperties>(
64 std::string("Incorrect file type: the Current File is not an")
65 + "EF Supersedes 6981h",
66 typeid(CardDataAccessException))},
67 {0x6A82,
68 std::make_shared<StatusProperties>(
69 "File not found", typeid(CardDataAccessException))},
70 {0x6A83,
71 std::make_shared<StatusProperties>(
72 "Offset not in the file (offset overflow)",
73 typeid(CardDataAccessException))},
74 {0x6B00,
75 std::make_shared<StatusProperties>(
76 "P1 value not supported",
77 typeid(CardIllegalParameterException))}});
78 return m;
79 }();
80
81CommandReadBinary::CommandReadBinary(
82 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
83 transactionContext,
84 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
85 std::uint8_t sfi,
86 int offset,
87 int length)
88: Command(
89 CardCommandRef::READ_BINARY,
90 std::unique_ptr<int>(new int(length)),
91 transactionContext,
92 commandContext)
93, mSfi(sfi)
94, mOffset(offset)
95{
96 const std::uint8_t cardClass
97 = transactionContext->getCard() != nullptr
98 ? transactionContext->getCard()->getCardClass().getValue()
99 : CalypsoCardClass::ISO.getValue();
100 mIsPreOpenMode
101 = transactionContext->getCard() != nullptr
102 && transactionContext->getCard()->getPreOpenWriteAccessLevel()
103 != WriteAccessLevel::UNKOWN;
104
105 std::uint8_t msb = static_cast<std::uint8_t>(
106 offset >> std::numeric_limits<std::uint8_t>::digits);
107 std::uint8_t lsb = static_cast<std::uint8_t>(offset & 0xFF);
108
109 /*
110 * 100xxxxx : 'xxxxx' = SFI of the EF to select.
111 * 0xxxxxxx : 'xxxxxxx' = MSB of the offset of the first byte.
112 */
113 const std::uint8_t p1 = msb > 0 ? msb : (0x80 + sfi);
114
115 /* APDU Case 2 */
116 setApduRequestInBestEffortMode(
117 std::make_shared<DtoAdapters::ApduRequestAdapter>(ApduUtil::build(
118 cardClass,
119 getCommandRef().getInstructionByte(),
120 p1,
121 lsb,
122 static_cast<uint8_t>(length))));
123
124 addSubName(
125 std::string("SFI: ") + HexUtil::toHex(sfi) + "h," + "Offset: "
126 + std::to_string(offset) + "," + "Length: " + std::to_string(length));
127}
128
129void
130CommandReadBinary::finalizeRequest()
131{
132 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
133}
134
135bool
136CommandReadBinary::isCryptoServiceRequiredToFinalizeRequest() const
137{
138 return getCommandContext()->isEncryptionActive();
139}
140
141bool
142CommandReadBinary::synchronizeCryptoServiceBeforeCardProcessing()
143{
144 if (!getCommandContext()->isSecureSessionOpen()) {
145 return true; /* Nothing to synchronize */
146 }
147
148 if (getCommandContext()->isEncryptionActive()) {
149 return false;
150 }
151
152 if (!mIsPreOpenMode) {
153 return false;
154 }
155
156 /* Pre-open mode without encryption in secure session */
157 if (!isCryptoServiceSynchronized()) {
158 const std::vector<std::uint8_t> anticipatedApduResponse
159 = buildAnticipatedResponse();
160 if (anticipatedApduResponse.empty()) {
161 const std::string sfiHex = HexUtil::toHex(mSfi);
162 mLogger->warn(
163 std::string("Unable to determine anticipated APDU response ")
164 + "because the record or some records have not been read "
165 + "beforehand [command={}, sfi={}, offset={}, length={}]",
166 getName(),
167 sfiHex,
168 mOffset,
169 getExpectedResponseLength());
170 return false;
171 }
172
173 mAnticipatedDataOut = Arrays::copyOf(
174 anticipatedApduResponse, anticipatedApduResponse.size() - 2);
175 updateTerminalSessionIfNeeded(anticipatedApduResponse);
176 }
177
178 return true;
179}
180
181std::vector<std::uint8_t>
182CommandReadBinary::buildAnticipatedResponse() const
183{
184 std::shared_ptr<ElementaryFile> ef
185 = getTransactionContext()->getCard()->getFileBySfi(mSfi);
186 if (ef == nullptr) {
187 return {};
188 }
189
190 try {
191 const std::vector<std::uint8_t> content = ef->getData()->getContent(
192 1,
193 static_cast<uint8_t>(mOffset),
194 static_cast<uint8_t>(*getExpectedResponseLength()));
195 std::vector<std::uint8_t> apdu(*getExpectedResponseLength() + 2);
196 /* Record content */
197 System::arraycopy(content, 0, apdu, 0, *getExpectedResponseLength());
198 /* SW 9000 */
199 apdu[*getExpectedResponseLength()] = 0x90;
200 return apdu;
201
202 } catch (const IndexOutOfBoundsException&) {
203 /* NOP */
204 }
205
206 return {};
207}
208
209void
210CommandReadBinary::parseResponse(std::shared_ptr<ApduResponseApi> apduResponse)
211{
212 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
213 if (!setApduResponseAndCheckStatusInBestEffortMode(apduResponse)) {
214 return;
215 }
216
217 getTransactionContext()->getCard()->setContent(
218 mSfi, 1, apduResponse->getDataOut(), mOffset);
219 if (!isCryptoServiceSynchronized()) {
220 updateTerminalSessionIfNeeded();
221 } else if (
222 getCommandContext()->isSecureSessionOpen() && mIsPreOpenMode
223 && !Arrays::equals(apduResponse->getDataOut(), mAnticipatedDataOut)) {
224 throw CardSecurityContextException(
225 "Data out does not match the anticipated data out",
226 CardCommandRef::READ_BINARY);
227 }
228}
229
230const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
231CommandReadBinary::getStatusTable() const
232{
233 return STATUS_TABLE;
234}
235
236} /* namespace calypso */
237} /* namespace card */
238} /* namespace keyple */