Skip to content

Commit cbcb108

Browse files
committed
Create SmartCard::Session object
Signed-off-by: Raul Metsma <raul@metsma.ee>
1 parent 708eb00 commit cbcb108

File tree

16 files changed

+311
-279
lines changed

16 files changed

+311
-279
lines changed

.github/workflows/cmake-windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
with:
2020
vcpkgArguments: gtest:x64-windows openssl:x64-windows
2121
vcpkgTriplet: x64-windows
22-
vcpkgGitCommitId: 0d5cae153065957df7f382de7c1549ccc88027e5
22+
vcpkgGitCommitId: 031ad89ce6c575df35a8e58707ad2c898446c63e
2323

2424
- name: Configure CMake
2525
run: cmake -A x64 "-DCMAKE_TOOLCHAIN_FILE=${env:VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" "-DCMAKE_BUILD_TYPE=${env:BUILD_TYPE}" -S . -B build

include/electronic-id/electronic-id.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ class ElectronicID
9898
virtual std::string name() const = 0;
9999
virtual Type type() const = 0;
100100

101-
virtual pcsc_cpp::SmartCard const& smartcard() const { return *card; }
101+
virtual pcsc_cpp::SmartCard const& smartcard() const { return card; }
102102

103103
protected:
104-
ElectronicID(pcsc_cpp::SmartCard::ptr _card) : card(std::move(_card)) {}
104+
ElectronicID(pcsc_cpp::SmartCard&& _card) noexcept : card(std::move(_card)) {}
105105

106-
pcsc_cpp::SmartCard::ptr card;
106+
pcsc_cpp::SmartCard card;
107107
};
108108

109109
using ElectronicIDConstructor = std::function<ElectronicID::ptr(const pcsc_cpp::Reader&)>;

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

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
#include <vector>
3131

3232
// The rule of five (C++ Core guidelines C.21).
33-
#define PCSC_CPP_DISABLE_COPY_MOVE(Class) \
33+
#define PCSC_CPP_DISABLE_COPY(Class) \
3434
Class(const Class&) = delete; \
35-
Class& operator=(const Class&) = delete; \
35+
Class& operator=(const Class&) = delete
36+
#define PCSC_CPP_DISABLE_COPY_MOVE(Class) \
37+
PCSC_CPP_DISABLE_COPY(Class); \
3638
Class(Class&&) = delete; \
3739
Class& operator=(Class&&) = delete
3840

@@ -235,7 +237,19 @@ struct CommandApdu
235237

236238
/** Opaque class that wraps the PC/SC smart card resources like card handle and I/O protocol. */
237239
class CardImpl;
238-
using CardImplPtr = std::unique_ptr<CardImpl>;
240+
241+
class SmartCard;
242+
243+
/** Reader provides card reader information, status and gives access to the smart card in it. */
244+
struct Reader
245+
{
246+
[[nodiscard]] SmartCard connectToCard() const;
247+
248+
const ContextPtr ctx;
249+
const string_t name;
250+
const byte_vector cardAtr;
251+
const bool isCardPresent = false;
252+
};
239253

240254
/** PIN pad PIN entry timer timeout */
241255
constexpr uint8_t PIN_PAD_PIN_ENTRY_TIMEOUT = 90; // 1 minute, 30 seconds
@@ -246,52 +260,37 @@ class SmartCard
246260
public:
247261
enum class Protocol { UNDEFINED, T0, T1 }; // AUTO = T0 | T1
248262

249-
using ptr = std::unique_ptr<SmartCard>;
250-
251-
class TransactionGuard
263+
class Session
252264
{
253265
public:
254-
TransactionGuard(const CardImpl& CardImpl, bool& inProgress);
255-
~TransactionGuard() noexcept;
256-
PCSC_CPP_DISABLE_COPY_MOVE(TransactionGuard);
266+
Session(const CardImpl& CardImpl);
267+
~Session() noexcept;
268+
PCSC_CPP_DISABLE_COPY_MOVE(Session);
269+
270+
ResponseApdu transmit(const CommandApdu& command) const;
271+
ResponseApdu transmitCTL(const CommandApdu& command, uint16_t lang, uint8_t minlen) const;
272+
bool readerHasPinPad() const;
257273

258274
private:
259275
const CardImpl& card;
260-
bool& inProgress;
261276
};
262277

263-
SmartCard(ContextPtr context, string_t readerName, byte_vector atr);
264-
SmartCard(); // Null object constructor.
278+
SmartCard(Reader _reader);
279+
SmartCard() noexcept; // Null object constructor.
280+
SmartCard(SmartCard&& other) noexcept;
265281
~SmartCard() noexcept;
266-
PCSC_CPP_DISABLE_COPY_MOVE(SmartCard);
282+
PCSC_CPP_DISABLE_COPY(SmartCard);
283+
SmartCard& operator=(SmartCard&& other) noexcept = delete;
267284

268-
TransactionGuard beginTransaction();
269-
ResponseApdu transmit(const CommandApdu& command) const;
270-
ResponseApdu transmitCTL(const CommandApdu& command, uint16_t lang, uint8_t minlen) const;
285+
[[nodiscard]] Session beginSession() const;
271286
bool readerHasPinPad() const;
272-
273-
Protocol protocol() const { return _protocol; }
274-
const byte_vector& atr() const { return _atr; }
275-
const string_t& readerName() const { return _readerName; }
287+
Protocol protocol() const;
288+
const byte_vector& atr() const { return reader.cardAtr; }
289+
const string_t& readerName() const { return reader.name; }
276290

277291
private:
278-
ContextPtr ctx;
279-
CardImplPtr card;
280-
string_t _readerName;
281-
byte_vector _atr;
282-
Protocol _protocol = Protocol::UNDEFINED;
283-
bool transactionInProgress = false;
284-
};
285-
286-
/** Reader provides card reader information, status and gives access to the smart card in it. */
287-
struct Reader
288-
{
289-
SmartCard::ptr connectToCard() const { return std::make_unique<SmartCard>(ctx, name, cardAtr); }
290-
291-
const ContextPtr ctx;
292-
const string_t name;
293-
const byte_vector cardAtr;
294-
const bool isCardPresent;
292+
Reader reader;
293+
std::unique_ptr<CardImpl> card;
295294
};
296295

297296
/**
@@ -304,10 +303,12 @@ std::vector<Reader> listReaders();
304303
// Utility functions.
305304

306305
/** Transmit APDU command and verify that expected response is received. */
307-
void transmitApduWithExpectedResponse(const SmartCard& card, const CommandApdu& command);
306+
void transmitApduWithExpectedResponse(const SmartCard::Session& session,
307+
const CommandApdu& command);
308308

309309
/** Read lenght bytes from currently selected binary file in blockLength-sized chunks. */
310-
byte_vector readBinary(const SmartCard& card, const uint16_t length, byte_type blockLength = 0x00);
310+
byte_vector readBinary(const SmartCard::Session& session, const uint16_t length,
311+
byte_type blockLength = 0x00);
311312

312313
// Errors.
313314

lib/libpcsc-cpp/src/SmartCard.cpp

Lines changed: 64 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -41,35 +41,6 @@
4141
namespace
4242
{
4343

44-
using namespace pcsc_cpp;
45-
46-
constexpr SmartCard::Protocol convertToSmartCardProtocol(const DWORD protocol)
47-
{
48-
switch (protocol) {
49-
case SCARD_PROTOCOL_UNDEFINED:
50-
return SmartCard::Protocol::UNDEFINED;
51-
case SCARD_PROTOCOL_T0:
52-
return SmartCard::Protocol::T0;
53-
case SCARD_PROTOCOL_T1:
54-
return SmartCard::Protocol::T1;
55-
default:
56-
THROW(Error, "Unsupported card protocol: " + std::to_string(protocol));
57-
}
58-
}
59-
60-
std::pair<SCARDHANDLE, DWORD> connectToCard(const SCARDCONTEXT ctx, const string_t& readerName)
61-
{
62-
const unsigned requestedProtocol =
63-
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; // Let PCSC auto-select protocol.
64-
DWORD protocolOut = SCARD_PROTOCOL_UNDEFINED;
65-
SCARDHANDLE cardHandle = 0;
66-
67-
SCard(Connect, ctx, readerName.c_str(), DWORD(SCARD_SHARE_SHARED), requestedProtocol,
68-
&cardHandle, &protocolOut);
69-
70-
return {cardHandle, protocolOut};
71-
}
72-
7344
template <class K, class V = uint32_t, class D, size_t dsize, typename Func>
7445
constexpr std::map<K, V> parseTLV(const std::array<D, dsize>& data, DWORD size, Func transform)
7546
{
@@ -93,21 +64,34 @@ constexpr uint32_t OMNIKEY_6121 = 0x6632;
9364
namespace pcsc_cpp
9465
{
9566

67+
SmartCard Reader::connectToCard() const
68+
{
69+
return {*this};
70+
}
71+
9672
class CardImpl
9773
{
9874
public:
99-
explicit CardImpl(std::pair<SCARDHANDLE, DWORD> cardParams) :
100-
cardHandle(cardParams.first), _protocol {cardParams.second, sizeof(SCARD_IO_REQUEST)}
75+
explicit CardImpl(const Reader& reader)
10176
{
102-
// TODO: debug("Protocol: " + to_string(protocol()))
77+
constexpr unsigned requestedProtocol =
78+
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; // Let PCSC auto-select protocol.
79+
SCard(Connect, reader.ctx->handle(), reader.name.c_str(), DWORD(SCARD_SHARE_SHARED),
80+
requestedProtocol, &cardHandle, &_protocol.dwProtocol);
81+
10382
try {
10483
DWORD size = 0;
105-
std::array<BYTE, 256> buf {};
106-
SCard(Control, cardHandle, DWORD(CM_IOCTL_GET_FEATURE_REQUEST), nullptr, 0U, buf.data(),
107-
DWORD(buf.size()), &size);
108-
features = parseTLV<DRIVER_FEATURES>(buf, size, [](uint32_t t) { return ntohl(t); });
84+
std::array<PCSC_TLV_STRUCTURE, FEATURE_CCID_ESC_COMMAND> list {};
85+
SCard(Control, cardHandle, DWORD(CM_IOCTL_GET_FEATURE_REQUEST), nullptr, 0U,
86+
list.data(), DWORD(list.size() * sizeof(PCSC_TLV_STRUCTURE)), &size);
87+
if (size % sizeof(PCSC_TLV_STRUCTURE))
88+
return;
89+
for (const auto& f : list) {
90+
features[DRIVER_FEATURES(f.tag)] = ntohl(f.value);
91+
}
10992

11093
if (auto ioctl = features.find(FEATURE_GET_TLV_PROPERTIES); ioctl != features.cend()) {
94+
std::array<BYTE, 256> buf {};
11195
SCard(Control, cardHandle, ioctl->second, nullptr, 0U, buf.data(),
11296
DWORD(buf.size()), &size);
11397
auto properties = parseTLV<TLV_PROPERTIES>(buf, size, [](uint32_t t) { return t; });
@@ -124,7 +108,7 @@ class CardImpl
124108
}
125109
}
126110

127-
~CardImpl()
111+
~CardImpl() noexcept
128112
{
129113
if (cardHandle) {
130114
// Cannot throw in destructor, so cannot use the SCard() macro here.
@@ -210,11 +194,24 @@ class CardImpl
210194

211195
void endTransaction() const { SCard(EndTransaction, cardHandle, DWORD(SCARD_LEAVE_CARD)); }
212196

213-
DWORD protocol() const { return _protocol.dwProtocol; }
197+
SmartCard::Protocol protocol() const
198+
{
199+
switch (_protocol.dwProtocol) {
200+
using enum SmartCard::Protocol;
201+
case SCARD_PROTOCOL_UNDEFINED:
202+
return UNDEFINED;
203+
case SCARD_PROTOCOL_T0:
204+
return T0;
205+
case SCARD_PROTOCOL_T1:
206+
return T1;
207+
default:
208+
THROW(Error, "Unsupported card protocol: " + std::to_string(_protocol.dwProtocol));
209+
}
210+
}
214211

215212
private:
216-
SCARDHANDLE cardHandle;
217-
const SCARD_IO_REQUEST _protocol;
213+
SCARDHANDLE cardHandle {};
214+
SCARD_IO_REQUEST _protocol {SCARD_PROTOCOL_UNDEFINED, sizeof(SCARD_IO_REQUEST)};
218215
std::map<DRIVER_FEATURES, uint32_t> features;
219216
uint32_t id_vendor {};
220217
uint32_t id_product {};
@@ -243,7 +240,8 @@ class CardImpl
243240
break;
244241
default:
245242
THROW(Error,
246-
"Error response: '" + response + "', protocol " + std::to_string(protocol()));
243+
"Error response: '" + response + "', protocol "
244+
+ std::to_string(_protocol.dwProtocol));
247245
}
248246

249247
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
@@ -271,64 +269,59 @@ class CardImpl
271269
}
272270
};
273271

274-
SmartCard::TransactionGuard::TransactionGuard(const CardImpl& card, bool& inProgress) :
275-
card(card), inProgress(inProgress)
272+
SmartCard::Session::Session(const CardImpl& card) : card(card)
276273
{
277274
card.beginTransaction();
278-
inProgress = true;
279275
}
280276

281-
SmartCard::TransactionGuard::~TransactionGuard() noexcept
277+
SmartCard::Session::~Session() noexcept
282278
{
283-
inProgress = false;
284279
try {
285280
card.endTransaction();
286281
} catch (...) {
287282
// Ignore exceptions in destructor.
288283
}
289284
}
290285

291-
SmartCard::SmartCard(ContextPtr context, string_t readerName, byte_vector atr) :
292-
ctx(std::move(context)),
293-
card(std::make_unique<CardImpl>(connectToCard(ctx->handle(), readerName))),
294-
_readerName(std::move(readerName)), _atr(std::move(atr)),
295-
_protocol(convertToSmartCardProtocol(card->protocol()))
286+
ResponseApdu SmartCard::Session::transmit(const CommandApdu& command) const
296287
{
297-
// TODO: debug("Card ATR -> " + bytes2hexstr(atr))
288+
return card.transmitBytes(command);
298289
}
299290

300-
SmartCard::SmartCard() = default;
301-
SmartCard::~SmartCard() noexcept = default;
291+
ResponseApdu SmartCard::Session::transmitCTL(const CommandApdu& command, uint16_t lang,
292+
uint8_t minlen) const
293+
{
294+
return card.transmitBytesCTL(command, lang, minlen);
295+
}
302296

303-
SmartCard::TransactionGuard SmartCard::beginTransaction()
297+
bool SmartCard::Session::readerHasPinPad() const
304298
{
305-
REQUIRE_NON_NULL(card)
306-
return {*card, transactionInProgress};
299+
return card.readerHasPinPad();
307300
}
308301

309-
bool SmartCard::readerHasPinPad() const
302+
SmartCard::SmartCard(Reader _reader) :
303+
reader(std::move(_reader)), card(std::make_unique<CardImpl>(reader))
310304
{
311-
return card ? card->readerHasPinPad() : false;
312305
}
313306

314-
ResponseApdu SmartCard::transmit(const CommandApdu& command) const
307+
SmartCard::SmartCard() noexcept = default;
308+
SmartCard::SmartCard(SmartCard&& other) noexcept = default;
309+
SmartCard::~SmartCard() noexcept = default;
310+
311+
SmartCard::Session SmartCard::beginSession() const
315312
{
316313
REQUIRE_NON_NULL(card)
317-
if (!transactionInProgress) {
318-
THROW(std::logic_error, "Call SmartCard::transmit() inside a transaction");
319-
}
320-
321-
return card->transmitBytes(command);
314+
return {*card};
322315
}
323316

324-
ResponseApdu SmartCard::transmitCTL(const CommandApdu& command, uint16_t lang, uint8_t minlen) const
317+
SmartCard::Protocol SmartCard::protocol() const
325318
{
326-
REQUIRE_NON_NULL(card)
327-
if (!transactionInProgress) {
328-
THROW(std::logic_error, "Call SmartCard::transmit() inside a transaction");
329-
}
319+
return card ? card->protocol() : Protocol::UNDEFINED;
320+
}
330321

331-
return card->transmitBytesCTL(command, lang, minlen);
322+
bool SmartCard::readerHasPinPad() const
323+
{
324+
return card ? card->readerHasPinPad() : false;
332325
}
333326

334327
} // namespace pcsc_cpp

lib/libpcsc-cpp/src/utils.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,23 @@ std::string operator+(std::string lhs, const byte_vector& rhs)
6767
return hexStringBuilder.str();
6868
}
6969

70-
void transmitApduWithExpectedResponse(const SmartCard& card, const CommandApdu& command)
70+
void transmitApduWithExpectedResponse(const SmartCard::Session& session, const CommandApdu& command)
7171
{
72-
const auto response = card.transmit(command);
72+
const auto response = session.transmit(command);
7373
if (!response.isOK()) {
7474
throw UnexpectedResponseError(command, response, __FILE__, __LINE__, __func__);
7575
}
7676
}
7777

78-
byte_vector readBinary(const SmartCard& card, const uint16_t length, byte_type blockLength)
78+
byte_vector readBinary(const SmartCard::Session& session, const uint16_t length,
79+
byte_type blockLength)
7980
{
8081
byte_vector resultBytes;
8182
resultBytes.reserve(length);
8283
while (resultBytes.size() < length) {
8384
byte_type chunk = byte_type(std::min<size_t>(length - resultBytes.size(), blockLength));
84-
auto response = card.transmit(CommandApdu::readBinary(uint16_t(resultBytes.size()), chunk));
85+
auto response =
86+
session.transmit(CommandApdu::readBinary(uint16_t(resultBytes.size()), chunk));
8587
if (chunk > 0 && response.data.size() != chunk) {
8688
THROW(Error,
8789
"Length mismatch, expected "s + std::to_string(chunk) + ", received "

0 commit comments

Comments
 (0)