Skip to content

Commit a751d92

Browse files
committed
Use CommandAPDU instead byte_vector
Signed-off-by: Raul Metsma <[email protected]>
1 parent 7f91f6a commit a751d92

File tree

2 files changed

+75
-85
lines changed

2 files changed

+75
-85
lines changed

lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,14 @@ struct CommandApdu
165165
d.push_back(le);
166166
}
167167

168-
virtual ~CommandApdu() noexcept = default;
168+
PCSC_CPP_CONSTEXPR_VECTOR CommandApdu(const CommandApdu& other, byte_type le) : d(other.d)
169+
{
170+
size_t pos = d.size() <= 5 ? 4 : 5 + d[4]; // Case 1/2 or 3/4
171+
d.resize(pos + 1);
172+
d[pos] = le;
173+
}
169174

170-
constexpr operator const byte_vector&() const { return d; }
175+
virtual ~CommandApdu() noexcept = default;
171176

172177
/**
173178
* A helper function to create a SELECT FILE command APDU.
@@ -249,6 +254,21 @@ struct CommandApdu
249254
return VerifyApdu {0x00, 0x20, 0x00, p2, std::move(pin)};
250255
}
251256

257+
/**
258+
* A helper function to create a GET RESPONSE command APDU.
259+
*
260+
* The ISO 7816-4 Section 7.1 GET RESPONSE command has the form:
261+
* CLA = 0x00
262+
* INS = 0xC0
263+
* P1, P2 = ‘0000’ (other values are RFU)
264+
* Lc and Data field = Empty
265+
* Le = Maximum length of data expected in response
266+
*/
267+
static PCSC_CPP_CONSTEXPR_VECTOR CommandApdu getResponse(byte_type le = 0x00)
268+
{
269+
return {0x00, 0xc0, 0x00, 0x00, le};
270+
}
271+
252272
byte_vector d;
253273
};
254274

lib/libpcsc-cpp/src/SmartCard.cpp

Lines changed: 53 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
#endif
3333

3434
#include <array>
35-
#include <map>
3635
#include <utility>
3736

3837
// TODO: Someday, maybe SCARD_SHARE_SHARED vs SCARD_SHARE_EXCLUSIVE and SCARD_RESET_CARD on
@@ -41,20 +40,6 @@
4140
namespace
4241
{
4342

44-
template <class K, class V = uint32_t, class D, size_t dsize, typename Func>
45-
constexpr std::map<K, V> parseTLV(const std::array<D, dsize>& data, DWORD size, Func transform)
46-
{
47-
std::map<K, V> result;
48-
for (auto p = data.cbegin(); DWORD(std::distance(data.cbegin(), p)) < size;) {
49-
auto tag = K(*p++);
50-
V value {};
51-
for (unsigned int i = 0, len = *p++; i < len; ++i)
52-
value |= V(*p++) << 8 * i;
53-
result[tag] = transform(value);
54-
}
55-
return result;
56-
}
57-
5843
constexpr uint32_t VENDOR_HID_GLOBAL = 0x076B;
5944
constexpr uint32_t OMNIKEY_3x21 = 0x3031;
6045
constexpr uint32_t OMNIKEY_6121 = 0x6632;
@@ -89,27 +74,29 @@ class CardImpl
8974

9075
try {
9176
DWORD size = 0;
92-
std::array<PCSC_TLV_STRUCTURE, FEATURE_CCID_ESC_COMMAND> list {};
9377
SCard(Control, cardHandle, DWORD(CM_IOCTL_GET_FEATURE_REQUEST), nullptr, 0U,
94-
list.data(), DWORD(list.size() * sizeof(PCSC_TLV_STRUCTURE)), &size);
78+
features.data(), DWORD(features.size() * sizeof(PCSC_TLV_STRUCTURE)), &size);
9579
if (size == 0 || size % sizeof(PCSC_TLV_STRUCTURE)) {
9680
return; // No features available or malformed response.
9781
}
98-
for (const auto& f : list) {
99-
features[DRIVER_FEATURES(f.tag)] = ntohl(f.value);
82+
for (auto& f : features) {
83+
f.value = ntohl(f.value);
10084
}
10185

102-
if (auto ioctl = features.find(FEATURE_GET_TLV_PROPERTIES); ioctl != features.cend()) {
86+
if (auto ioctl = feature(FEATURE_GET_TLV_PROPERTIES); ioctl != features.cend()) {
10387
std::array<BYTE, 256> buf {};
104-
SCard(Control, cardHandle, ioctl->second, nullptr, 0U, buf.data(),
105-
DWORD(buf.size()), &size);
106-
auto properties = parseTLV<TLV_PROPERTIES>(buf, size, [](uint32_t t) { return t; });
107-
if (auto vendor = properties.find(TLV_PROPERTY_wIdVendor);
108-
vendor != properties.cend())
109-
id_vendor = vendor->second;
110-
if (auto product = properties.find(TLV_PROPERTY_wIdProduct);
111-
product != properties.cend())
112-
id_product = product->second;
88+
SCard(Control, cardHandle, ioctl->value, nullptr, 0U, buf.data(), DWORD(buf.size()),
89+
&size);
90+
for (auto p = buf.cbegin(); DWORD(std::distance(buf.cbegin(), p)) < size;) {
91+
auto tag = TLV_PROPERTIES(*p++);
92+
uint32_t value {};
93+
for (unsigned int i = 0, len = *p++; i < len; ++i)
94+
value |= uint32_t(*p++) << 8 * i;
95+
if (tag == TLV_PROPERTY_wIdVendor)
96+
id_vendor = value;
97+
if (tag == TLV_PROPERTY_wIdProduct)
98+
id_product = value;
99+
}
113100
}
114101
} catch (const ScardError&) {
115102
// Ignore driver errors during card feature requests.
@@ -138,33 +125,20 @@ class CardImpl
138125
return false;
139126
if (getenv("SMARTCARDPP_NOPINPAD"))
140127
return false;
141-
return features.contains(FEATURE_VERIFY_PIN_START)
142-
|| features.contains(FEATURE_VERIFY_PIN_DIRECT);
128+
return feature(FEATURE_VERIFY_PIN_START) != features.cend()
129+
|| feature(FEATURE_VERIFY_PIN_DIRECT) != features.cend();
143130
}
144131

145-
ResponseApdu transmitBytes(const byte_vector& commandBytes) const
132+
ResponseApdu transmitBytes(const CommandApdu& commandApdu) const
146133
{
147134
byte_vector responseBytes(ResponseApdu::MAX_SIZE, 0);
148135
auto responseLength = DWORD(responseBytes.size());
149-
150-
// TODO: debug("Sending: " + bytes2hexstr(commandBytes))
151-
152-
SCard(Transmit, cardHandle, &_protocol, commandBytes.data(), DWORD(commandBytes.size()),
136+
SCard(Transmit, cardHandle, &_protocol, commandApdu.d.data(), DWORD(commandApdu.d.size()),
153137
nullptr, responseBytes.data(), &responseLength);
154-
155-
auto response = toResponse(std::move(responseBytes), responseLength);
156-
157-
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
158-
getResponseWithLE(response, commandBytes);
159-
}
160-
if (response.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
161-
getMoreResponseData(response);
162-
}
163-
164-
return response;
138+
return toResponse(std::move(responseBytes), responseLength);
165139
}
166140

167-
ResponseApdu transmitBytesCTL(const byte_vector& commandBytes, uint16_t lang,
141+
ResponseApdu transmitBytesCTL(const CommandApdu& commandApdu, uint16_t lang,
168142
uint8_t minlen) const
169143
{
170144
uint8_t PINFrameOffset = 0;
@@ -182,20 +156,20 @@ class CardImpl
182156
data->bNumberMessage = CCIDDefaultInvitationMessage;
183157
data->wLangId = lang;
184158
data->bMsgIndex = NoInvitationMessage;
185-
data->ulDataLength = uint32_t(commandBytes.size());
186-
cmd.insert(cmd.cend(), commandBytes.cbegin(), commandBytes.cend());
159+
data->ulDataLength = uint32_t(commandApdu.d.size());
160+
cmd.insert(cmd.cend(), commandApdu.d.cbegin(), commandApdu.d.cend());
187161

188-
DWORD ioctl =
189-
features.at(features.contains(FEATURE_VERIFY_PIN_START) ? FEATURE_VERIFY_PIN_START
190-
: FEATURE_VERIFY_PIN_DIRECT);
162+
auto ioctl = feature(FEATURE_VERIFY_PIN_START);
163+
if (feature(FEATURE_VERIFY_PIN_START) == features.cend())
164+
ioctl = feature(FEATURE_VERIFY_PIN_DIRECT);
191165
byte_vector responseBytes(ResponseApdu::MAX_SIZE, 0);
192166
auto responseLength = DWORD(responseBytes.size());
193-
SCard(Control, cardHandle, ioctl, cmd.data(), DWORD(cmd.size()),
167+
SCard(Control, cardHandle, ioctl->value, cmd.data(), DWORD(cmd.size()),
194168
LPVOID(responseBytes.data()), DWORD(responseBytes.size()), &responseLength);
195169

196-
if (auto finish = features.find(FEATURE_VERIFY_PIN_FINISH); finish != features.cend()) {
170+
if (auto finish = feature(FEATURE_VERIFY_PIN_FINISH); finish != features.cend()) {
197171
responseLength = DWORD(responseBytes.size());
198-
SCard(Control, cardHandle, finish->second, nullptr, 0U, LPVOID(responseBytes.data()),
172+
SCard(Control, cardHandle, finish->value, nullptr, 0U, LPVOID(responseBytes.data()),
199173
DWORD(responseBytes.size()), &responseLength);
200174
}
201175

@@ -224,10 +198,16 @@ class CardImpl
224198
private:
225199
SCARDHANDLE cardHandle {};
226200
SCARD_IO_REQUEST _protocol {SCARD_PROTOCOL_UNDEFINED, sizeof(SCARD_IO_REQUEST)};
227-
std::map<DRIVER_FEATURES, uint32_t> features;
201+
std::array<PCSC_TLV_STRUCTURE, FEATURE_CCID_ESC_COMMAND> features {};
228202
uint32_t id_vendor {};
229203
uint32_t id_product {};
230204

205+
constexpr decltype(features)::const_iterator feature(DRIVER_FEATURES tag) const
206+
{
207+
return std::find_if(features.cbegin(), features.cend(),
208+
[tag](const PCSC_TLV_STRUCTURE& tlv) { return tlv.tag == tag; });
209+
}
210+
231211
ResponseApdu toResponse(byte_vector&& responseBytes, size_t responseLength) const
232212
{
233213
if (responseLength > responseBytes.size()) {
@@ -266,31 +246,6 @@ class CardImpl
266246
+ std::to_string(_protocol.dwProtocol));
267247
}
268248
}
269-
270-
void getMoreResponseData(ResponseApdu& response) const
271-
{
272-
byte_vector getResponseCommand {0x00, 0xc0, 0x00, 0x00, 0x00};
273-
274-
ResponseApdu newResponse {response.sw1, response.sw2};
275-
276-
while (newResponse.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
277-
getResponseCommand[4] = newResponse.sw2;
278-
newResponse = transmitBytes(getResponseCommand);
279-
response.data.insert(response.data.end(), newResponse.data.cbegin(),
280-
newResponse.data.cend());
281-
}
282-
283-
response.sw1 = ResponseApdu::OK;
284-
response.sw2 = 0;
285-
}
286-
287-
void getResponseWithLE(ResponseApdu& response, byte_vector command) const
288-
{
289-
size_t pos = command.size() <= 5 ? 4 : 5 + command[4]; // Case 1/2 or 3/4
290-
command.resize(pos + 1);
291-
command[pos] = response.sw2;
292-
response = transmitBytes(command);
293-
}
294249
};
295250

296251
SmartCard::Session::Session(const CardImpl& card) : card(card)
@@ -309,7 +264,22 @@ SmartCard::Session::~Session() noexcept
309264

310265
ResponseApdu SmartCard::Session::transmit(const CommandApdu& command) const
311266
{
312-
return card.transmitBytes(command);
267+
auto response = card.transmitBytes(command);
268+
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
269+
response = card.transmitBytes(CommandApdu(command, response.sw2));
270+
}
271+
if (response.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
272+
auto getResponseCommand = CommandApdu::getResponse();
273+
while (response.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
274+
getResponseCommand.d[4] = response.sw2;
275+
auto newResponse = card.transmitBytes(getResponseCommand);
276+
response.sw1 = newResponse.sw1;
277+
response.sw2 = newResponse.sw2;
278+
response.data.insert(response.data.end(), newResponse.data.cbegin(),
279+
newResponse.data.cend());
280+
}
281+
}
282+
return response;
313283
}
314284

315285
ResponseApdu SmartCard::Session::transmitCTL(const CommandApdu& command, uint16_t lang,

0 commit comments

Comments
 (0)