Skip to content

Commit aa0e3d0

Browse files
committed
Use CommandAPDU instead byte_vector
WE2-1147 Signed-off-by: Raul Metsma <[email protected]>
1 parent 62e4f39 commit aa0e3d0

File tree

2 files changed

+80
-85
lines changed

2 files changed

+80
-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: 58 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
#include <arpa/inet.h>
3232
#endif
3333

34+
#include <algorithm>
3435
#include <array>
35-
#include <map>
3636
#include <utility>
3737

3838
// TODO: Someday, maybe SCARD_SHARE_SHARED vs SCARD_SHARE_EXCLUSIVE and SCARD_RESET_CARD on
@@ -41,20 +41,6 @@
4141
namespace
4242
{
4343

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-
5844
constexpr uint32_t VENDOR_HID_GLOBAL = 0x076B;
5945
constexpr uint32_t OMNIKEY_3x21 = 0x3031;
6046
constexpr uint32_t OMNIKEY_6121 = 0x6632;
@@ -89,27 +75,33 @@ class CardImpl
8975

9076
try {
9177
DWORD size = 0;
92-
std::array<PCSC_TLV_STRUCTURE, FEATURE_CCID_ESC_COMMAND> list {};
9378
SCard(Control, cardHandle, DWORD(CM_IOCTL_GET_FEATURE_REQUEST), nullptr, 0U,
94-
list.data(), DWORD(list.size() * sizeof(PCSC_TLV_STRUCTURE)), &size);
79+
features.data(), DWORD(features.size() * sizeof(PCSC_TLV_STRUCTURE)), &size);
9580
if (size == 0 || size % sizeof(PCSC_TLV_STRUCTURE)) {
9681
return; // No features available or malformed response.
9782
}
98-
for (const auto& f : list) {
99-
features[DRIVER_FEATURES(f.tag)] = ntohl(f.value);
83+
for (auto& f : features) {
84+
f.value = ntohl(f.value);
10085
}
10186

102-
if (auto ioctl = features.find(FEATURE_GET_TLV_PROPERTIES); ioctl != features.cend()) {
87+
if (auto ioctl = feature(FEATURE_GET_TLV_PROPERTIES); ioctl != features.cend()) {
10388
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;
89+
SCard(Control, cardHandle, ioctl->value, nullptr, 0U, buf.data(), DWORD(buf.size()),
90+
&size);
91+
for (auto p = buf.cbegin(); DWORD(std::distance(buf.cbegin(), p)) < size;) {
92+
auto tag = TLV_PROPERTIES(*p++);
93+
BYTE len = *p++;
94+
if (DWORD(std::distance(buf.cbegin(), p)) + len > size) {
95+
break; // Malformed TLV data.
96+
}
97+
uint32_t value {};
98+
for (BYTE i = 0; i < len; ++i)
99+
value |= uint32_t(*p++) << 8 * i;
100+
if (tag == TLV_PROPERTY_wIdVendor)
101+
id_vendor = value;
102+
if (tag == TLV_PROPERTY_wIdProduct)
103+
id_product = value;
104+
}
113105
}
114106
} catch (const ScardError&) {
115107
// Ignore driver errors during card feature requests.
@@ -138,33 +130,20 @@ class CardImpl
138130
return false;
139131
if (getenv("SMARTCARDPP_NOPINPAD"))
140132
return false;
141-
return features.contains(FEATURE_VERIFY_PIN_START)
142-
|| features.contains(FEATURE_VERIFY_PIN_DIRECT);
133+
return feature(FEATURE_VERIFY_PIN_START) != features.cend()
134+
|| feature(FEATURE_VERIFY_PIN_DIRECT) != features.cend();
143135
}
144136

145-
ResponseApdu transmitBytes(const byte_vector& commandBytes) const
137+
ResponseApdu transmitBytes(const CommandApdu& commandApdu) const
146138
{
147139
byte_vector responseBytes(ResponseApdu::MAX_SIZE, 0);
148140
auto responseLength = DWORD(responseBytes.size());
149-
150-
// TODO: debug("Sending: " + bytes2hexstr(commandBytes))
151-
152-
SCard(Transmit, cardHandle, &_protocol, commandBytes.data(), DWORD(commandBytes.size()),
141+
SCard(Transmit, cardHandle, &_protocol, commandApdu.d.data(), DWORD(commandApdu.d.size()),
153142
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;
143+
return toResponse(std::move(responseBytes), responseLength);
165144
}
166145

167-
ResponseApdu transmitBytesCTL(const byte_vector& commandBytes, uint16_t lang,
146+
ResponseApdu transmitBytesCTL(const CommandApdu& commandApdu, uint16_t lang,
168147
uint8_t minlen) const
169148
{
170149
uint8_t PINFrameOffset = 0;
@@ -182,20 +161,20 @@ class CardImpl
182161
data->bNumberMessage = CCIDDefaultInvitationMessage;
183162
data->wLangId = lang;
184163
data->bMsgIndex = NoInvitationMessage;
185-
data->ulDataLength = uint32_t(commandBytes.size());
186-
cmd.insert(cmd.cend(), commandBytes.cbegin(), commandBytes.cend());
164+
data->ulDataLength = uint32_t(commandApdu.d.size());
165+
cmd.insert(cmd.cend(), commandApdu.d.cbegin(), commandApdu.d.cend());
187166

188-
DWORD ioctl =
189-
features.at(features.contains(FEATURE_VERIFY_PIN_START) ? FEATURE_VERIFY_PIN_START
190-
: FEATURE_VERIFY_PIN_DIRECT);
167+
auto ioctl = feature(FEATURE_VERIFY_PIN_START);
168+
if (feature(FEATURE_VERIFY_PIN_START) == features.cend())
169+
ioctl = feature(FEATURE_VERIFY_PIN_DIRECT);
191170
byte_vector responseBytes(ResponseApdu::MAX_SIZE, 0);
192171
auto responseLength = DWORD(responseBytes.size());
193-
SCard(Control, cardHandle, ioctl, cmd.data(), DWORD(cmd.size()),
172+
SCard(Control, cardHandle, ioctl->value, cmd.data(), DWORD(cmd.size()),
194173
LPVOID(responseBytes.data()), DWORD(responseBytes.size()), &responseLength);
195174

196-
if (auto finish = features.find(FEATURE_VERIFY_PIN_FINISH); finish != features.cend()) {
175+
if (auto finish = feature(FEATURE_VERIFY_PIN_FINISH); finish != features.cend()) {
197176
responseLength = DWORD(responseBytes.size());
198-
SCard(Control, cardHandle, finish->second, nullptr, 0U, LPVOID(responseBytes.data()),
177+
SCard(Control, cardHandle, finish->value, nullptr, 0U, LPVOID(responseBytes.data()),
199178
DWORD(responseBytes.size()), &responseLength);
200179
}
201180

@@ -224,10 +203,16 @@ class CardImpl
224203
private:
225204
SCARDHANDLE cardHandle {};
226205
SCARD_IO_REQUEST _protocol {SCARD_PROTOCOL_UNDEFINED, sizeof(SCARD_IO_REQUEST)};
227-
std::map<DRIVER_FEATURES, uint32_t> features;
206+
std::array<PCSC_TLV_STRUCTURE, FEATURE_CCID_ESC_COMMAND> features {};
228207
uint32_t id_vendor {};
229208
uint32_t id_product {};
230209

210+
constexpr decltype(features)::const_iterator feature(DRIVER_FEATURES tag) const
211+
{
212+
return std::find_if(features.cbegin(), features.cend(),
213+
[tag](PCSC_TLV_STRUCTURE tlv) { return tlv.tag == tag; });
214+
}
215+
231216
ResponseApdu toResponse(byte_vector&& responseBytes, size_t responseLength) const
232217
{
233218
if (responseLength > responseBytes.size()) {
@@ -266,31 +251,6 @@ class CardImpl
266251
+ std::to_string(_protocol.dwProtocol));
267252
}
268253
}
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-
}
294254
};
295255

296256
SmartCard::Session::Session(const CardImpl& card) : card(card)
@@ -309,7 +269,22 @@ SmartCard::Session::~Session() noexcept
309269

310270
ResponseApdu SmartCard::Session::transmit(const CommandApdu& command) const
311271
{
312-
return card.transmitBytes(command);
272+
auto response = card.transmitBytes(command);
273+
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
274+
response = card.transmitBytes(CommandApdu(command, response.sw2));
275+
}
276+
if (response.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
277+
auto getResponseCommand = CommandApdu::getResponse();
278+
while (response.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
279+
getResponseCommand.d[4] = response.sw2;
280+
auto newResponse = card.transmitBytes(getResponseCommand);
281+
response.sw1 = newResponse.sw1;
282+
response.sw2 = newResponse.sw2;
283+
response.data.insert(response.data.end(), newResponse.data.cbegin(),
284+
newResponse.data.cend());
285+
}
286+
}
287+
return response;
313288
}
314289

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

0 commit comments

Comments
 (0)