This library provides a simple and nice C++ wrapper around these libraries, so that programmers can concentrate on functionality. It offers general support for PCSC-lite, OpenSSL, PKCS#11, plus specific functionality for the SuisseID.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1059 lines
37 KiB
1059 lines
37 KiB
/*! @file |
|
|
|
This file offers a C++ access to the PCSC library. |
|
|
|
@id $Id$ |
|
*/ |
|
// 1 2 3 4 5 6 7 8 |
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
|
|
#ifndef PCSC_HXX |
|
#define PCSC_HXX |
|
|
|
#include <cryptaux.hxx> |
|
|
|
#include <string> |
|
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) |
|
#undef UNICODE |
|
#undef DATADIR |
|
#include <winscard.h> |
|
#undef ERROR |
|
#ifndef MAX_ATR_SIZE |
|
#define MAX_ATR_SIZE 33 |
|
#endif |
|
namespace pcsc { |
|
|
|
#ifdef UNICODE |
|
inline std::wstring strconv(std::string s) { |
|
return std::wstring(s.begin(), s.end()); |
|
} |
|
inline std::string strconv(std::wstring s) { |
|
return std::string(s.begin(), s.end()); |
|
} |
|
typedef wchar_t char_t; |
|
typedef std::wstring string; |
|
#else |
|
inline const std::string& strconv(const std::string& s) { |
|
return s; |
|
} |
|
typedef char char_t; |
|
typedef std::string string; |
|
#endif |
|
} |
|
#else |
|
#include <PCSC/pcsclite.h> |
|
#include <PCSC/wintypes.h> |
|
#include <PCSC/winscard.h> |
|
namespace pcsc { |
|
inline const std::string& strconv(const std::string& s) { |
|
return s; |
|
} |
|
typedef char char_t; |
|
typedef std::string string; |
|
} |
|
#endif |
|
|
|
#include <vector> |
|
#include <map> |
|
#include <mrw/checkcxx11.hxx> |
|
#include <memory> |
|
|
|
#include <sstream> |
|
#include <iomanip> |
|
|
|
/*! @defgroup gpcsc C++ Wrapper around pcsc-lite API |
|
|
|
This library is a C++ wrapper to the awful pcsc-lite interface. |
|
The reason for this wrapper is to get a nice object oriented |
|
interface written in C++ manner and using standard types and so to |
|
avoid the ugly M$-C-quirks. This interface is memory clean. |
|
@todo: Not implemented, not supported: |
|
- SCardGetStatusChange(hContext, 0, rgReaderStates, 1); |
|
- several other |
|
I do not need these, and I don't know the usecase where they are |
|
required. If you need something that is not yet supported, please |
|
let me know, what you need and why. Then I'll add it so that it |
|
best fits the common needs. */ |
|
//@{ |
|
/*! @defgroup pcsclib PCSC C++ Library */ |
|
/*! @defgroup pcscexceptions PCSC Exceptions */ |
|
|
|
//! @ref gpcsc @copydoc gpcsc |
|
namespace pcsc { |
|
|
|
//============================================================================ |
|
//! @addtogroup pcscexceptions |
|
//@{ |
|
|
|
//---------------------------------------------------------------------------- |
|
class exception: public std::exception { |
|
public: |
|
exception(const std::string& reason) throw(): _what("pcsc: "+reason) { |
|
CRYPTOLOG("ERROR: "<<what()); |
|
} |
|
~exception() throw() {} |
|
const char* what() const throw() { |
|
return _what.c_str(); |
|
} |
|
private: |
|
std::string _what; |
|
}; |
|
//---------------------------------------------------------------------------- |
|
class not_implemented: public exception { |
|
public: |
|
not_implemented(const std::string& reason) throw(): |
|
exception("feature is not implemented: "+reason) { |
|
} |
|
}; |
|
//---------------------------------------------------------------------------- |
|
class access_error: public exception { |
|
public: |
|
access_error(const std::string& reason) throw(): |
|
exception("smardcard access error: "+reason) { |
|
} |
|
}; |
|
//---------------------------------------------------------------------------- |
|
class wrong_pin: public access_error { |
|
public: |
|
wrong_pin(const std::string& reason) throw(): |
|
access_error("wrong pin: "+reason) { |
|
} |
|
}; |
|
//---------------------------------------------------------------------------- |
|
class runtime_error: public exception { |
|
public: |
|
runtime_error(const std::string& reason, const std::string& data) throw(): |
|
exception("runtime error, "+reason+": "+crypto::hex(data)) {} |
|
}; |
|
//---------------------------------------------------------------------------- |
|
class neesting_error: public exception { |
|
public: |
|
neesting_error() throw(): |
|
exception("neesting error: more endTransaction than beginTransaction") |
|
{} |
|
}; |
|
//@} |
|
|
|
//! @addtogroup pcsclib |
|
//@{ |
|
|
|
//============================================================================ |
|
class Connection { |
|
|
|
//...............................................................typedefs |
|
public: |
|
|
|
|
|
//------------------------------------------------------------------Reader |
|
friend class Reader; |
|
class Reader { |
|
|
|
//............................................................typedefs |
|
public: |
|
|
|
friend class Transaction; |
|
//! Scoped transaction. |
|
/*! This is a scoped transaction. That means, the |
|
transaction is started in the constructor and ended in |
|
the destructor, unless it is already ended before. In |
|
case of an exception or leaving the block, the |
|
transaction is always cleaned up. |
|
|
|
There's a neesting counter to make sure, transaction is |
|
started only once and ended only once, even though the |
|
locking can be neested. |
|
|
|
@code |
|
pcsc::Connection c; |
|
{ |
|
Transaction t(c.reader("name")); |
|
[...] // do some stuff, possible exceptions thrown |
|
if (problem) return; // automatically ended |
|
[...] |
|
t.end(); // ended if we reach this line |
|
[...] |
|
} // also ended, unless commit reaced |
|
@endcode */ |
|
class Transaction { |
|
public: |
|
//! Begins a transaction. |
|
/*! @note Please note that the Reader is required in the |
|
destructor und must therefore live longer than the |
|
Transaction instance. */ |
|
Transaction(std::shared_ptr<Reader> r): |
|
_reader(r), _running(true) { |
|
CRYPTOLOG("log"); |
|
_reader->beginTransaction(); |
|
} |
|
//! Cancels the transaction if not yet finished. |
|
~Transaction() try { |
|
CRYPTOLOG("log"); |
|
end(); |
|
} catch (...) { |
|
if (!std::uncaught_exception()) throw; |
|
} |
|
//! Ends the running transaction. |
|
void end() { |
|
if (_running) _reader->endTransaction(); |
|
_running = false; |
|
} |
|
private: |
|
std::shared_ptr<Reader> _reader; |
|
bool _running; |
|
}; |
|
|
|
//! State and attribute list of a reader. |
|
class Status { |
|
public: |
|
Status(DWORD s, const std::string& a): |
|
state(s), atr(a) {} |
|
const DWORD state; |
|
const std::string atr; |
|
}; |
|
|
|
//.............................................................methods |
|
public: |
|
|
|
//! Disconnects connection. |
|
~Reader() { |
|
CRYPTOLOG("Disconnect Reader"); |
|
_state = SCardDisconnect(_id, SCARD_RESET_CARD); |
|
if (!std::uncaught_exception()) |
|
_connection->check(_state, "disconnect smartcard"); |
|
} |
|
|
|
//! Get reader status. |
|
Status status() { |
|
DWORD dummy(0); |
|
DWORD s; |
|
DWORD len(MAX_ATR_SIZE); |
|
unsigned char a[MAX_ATR_SIZE]; |
|
check(SCardStatus(_id, 0, &dummy, &s, &_protocol, a, &len), |
|
"query smartcard status"); |
|
return Status(s, std::string((char*)a, len)); |
|
} |
|
|
|
//! Transmit data to reader. |
|
/*! @note Take care: Stings may contain embedded @c 0. */ |
|
std::string transmit(char cla, char ins, char p1, char p2, |
|
const std::string& lc = std::string(), |
|
unsigned char le = 253) { |
|
std::string claInsP1P2; |
|
claInsP1P2.push_back(cla); |
|
claInsP1P2.push_back(ins); |
|
claInsP1P2.push_back(p1); |
|
claInsP1P2.push_back(p2); |
|
return transmit(claInsP1P2, lc, le); |
|
} |
|
|
|
//! Transmit data to reader. |
|
/*! @note Take care: Stings may contain embedded @c 0. */ |
|
std::string transmit(char cla, char ins, char p1, char p2, |
|
unsigned char le) { |
|
std::string claInsP1P2; |
|
claInsP1P2.push_back(cla); |
|
claInsP1P2.push_back(ins); |
|
claInsP1P2.push_back(p1); |
|
claInsP1P2.push_back(p2); |
|
return transmit(claInsP1P2, std::string(), le); |
|
} |
|
|
|
//! Transmit data to reader. |
|
/*! @note Take care: Stings may contain embedded @c 0. */ |
|
std::string transmit(char cla, char ins, char p1, char p2, |
|
const char* lc, int len, |
|
unsigned char le = 253) { |
|
std::string claInsP1P2; |
|
claInsP1P2.push_back(cla); |
|
claInsP1P2.push_back(ins); |
|
claInsP1P2.push_back(p1); |
|
claInsP1P2.push_back(p2); |
|
return transmit(claInsP1P2, std::string(lc, len), le); |
|
} |
|
|
|
//! Transmit data to reader. |
|
/*! @note Take care: Stings may contain embedded @c 0. |
|
@note Prefer the transmit methods that passes @c cla, @c |
|
ins, @c p1 and @c p2 separate.*/ |
|
std::string transmit(const std::string& claInsP1P2, |
|
const std::string& lc, |
|
unsigned char le = 253) { |
|
if (claInsP1P2.size()!=4) |
|
throw runtime_error("transmit: claInsP1P2 must be 4 byte", |
|
claInsP1P2); |
|
if (lc.size()>255) throw runtime_error("transmit: lc too long", lc); |
|
std::string msg(claInsP1P2); |
|
if (lc.size()) (msg+=(char)lc.size())+=lc; |
|
msg+=le; |
|
return transmit(msg); |
|
} |
|
|
|
//! Transmit data to reader. |
|
/*! @note Take care: Stings may contain embedded @c 0. |
|
@note Prefer the transmit methods that passes @c cla, @c |
|
ins, @c p1 and @c p2 separate.*/ |
|
std::string transmit(std::string in) { |
|
const DWORD bufflen(1024); |
|
DWORD len(bufflen); // arbitrary |
|
unsigned char buff[bufflen]; |
|
SCARD_IO_REQUEST rPci; |
|
rPci.dwProtocol = pci()->dwProtocol; |
|
rPci.cbPciLength = sizeof(rPci); |
|
// log only in verbose debuggung; could log pins |
|
CRYPTOLOG_VERBOSE("SCardTransmit: "<<crypto::hex(in)); |
|
try { |
|
check(SCardTransmit(_id, &rPci, |
|
(LPCBYTE)in.c_str(), |
|
in.size(), |
|
0, buff, &len), |
|
"smartcard transmit message "+crypto::hex(in)); |
|
} catch (std::exception& x) { |
|
CRYPTOLOG("failed with "<<x.what()); |
|
// try to fix Apple's Mac OS X 10.10 implementation bug |
|
# ifdef __APPLE__ |
|
/*! @bug Work around Mac OSX 10.10 bug. On Mac OSX |
|
10.10 there is a bug in PCSC: After a |
|
reconnect, first transaction (SCardTransmit) |
|
fails with SCARD_W_RESET_CARD |
|
(0x80100068). */ |
|
for (int cnt(0); cnt<10 && _state==SCARD_W_RESET_CARD; ++cnt) { |
|
// just try to resend |
|
CRYPTOLOG("Mac OS X 10.10 implementation bug: " |
|
"On Mac OSX 10.10 there is a bug in " |
|
"PCSC: After a reconnect, first " |
|
"transaction (SCardTransmit) fails " |
|
"with SCARD_W_RESET_CARD (0x80100068). " |
|
"Retry Nr. "<<cnt); |
|
try { |
|
reconnect(); |
|
if (check(SCardTransmit(_id, &rPci, |
|
(LPCBYTE)in.c_str(), |
|
in.size(), |
|
0, buff, &len), |
|
"smartcard transmit resend message " |
|
+crypto::hex(in))) { |
|
CRYPTOLOG("successful after retry nr. "<<cnt); |
|
return std::string((char*)buff, len); |
|
} |
|
} catch (std::exception& x) { |
|
CRYPTOLOG("failed again with "<<x.what()); |
|
} |
|
} |
|
# endif |
|
CRYPTOLOG("definitely failed with "<<x.what()); |
|
throw; // just rethrow otherwise |
|
} |
|
//CRYPTOLOG(" -> "<<crypto::hex(std::string((char*)buff, len))); |
|
return std::string((char*)buff, len); |
|
} |
|
|
|
//! Transmit control command and data to the reader. |
|
/*! |
|
* @note Take care: Strings may contain embedded @c 0. |
|
*/ |
|
std::string control(DWORD controlCode, |
|
std::string in) { |
|
DWORD len(256); // arbitrary |
|
UCHAR dataBuffer[256]; |
|
CRYPTOLOG("SCardControl: "<<"Command: "<<controlCode); |
|
CRYPTOLOG(" -> "<<crypto::hex(in)); |
|
check(SCardControl(_id, controlCode, |
|
(LPCBYTE)in.c_str(), in.size(), |
|
dataBuffer, sizeof(dataBuffer), &len), |
|
"smartcard control message sent"); |
|
CRYPTOLOG(" -> "<<crypto::hex(std::string((char*)dataBuffer, len))); |
|
return std::string((char*)dataBuffer, len); |
|
} |
|
|
|
//! @c false if last operation was not successful |
|
operator bool() const { |
|
// Values are 32 bit values layed out as follows: |
|
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 |
|
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 |
|
// ---+-+-+-----------------------+------------------------------- |
|
// Sev|C|R| Facility | Code |
|
// ---+-+-+-----------------------+------------------------------- |
|
// where Sev - is the severity code |
|
// 00 - Success |
|
// 01 - Informational |
|
// 10 - Warning |
|
// 11 - Error |
|
// So everything with Sev=00 is successful |
|
// theoretically even with Sev=01, but that's still rejected |
|
//return (_state>>30&3)==0; |
|
return _state==SCARD_S_SUCCESS; |
|
} |
|
|
|
//...........................................................variables |
|
public: |
|
|
|
const std::string name; |
|
|
|
//.............................................................methods |
|
private: |
|
|
|
//! Use scoped transactions with @ref Transaction |
|
/*! Calls @c SCardBeginTransaction unless it's already inside |
|
a transaction. */ |
|
void beginTransaction() { |
|
if (++_neesting==1) { |
|
CRYPTOLOG("open transaction"); |
|
check(SCardBeginTransaction(_id), "smartcard begin transaction"); |
|
} |
|
} |
|
|
|
//! Use scoped transactions with @ref Transaction |
|
/*! Calls @c SCardEndTransaction if it's inside a transaction. |
|
|
|
@throws neesting_error if there are more calls to @c |
|
endTransaction than to @c beginTransaction. */ |
|
void endTransaction() { |
|
if (--_neesting==0) { |
|
CRYPTOLOG("close transaction"); |
|
check(SCardEndTransaction(_id, SCARD_LEAVE_CARD), |
|
"smartcard end transaction"); |
|
} else if (_neesting<0) { |
|
_neesting = 0; |
|
throw neesting_error(); |
|
} |
|
} |
|
|
|
/*! @throw not_implemented if _protocol is unknown. */ |
|
const SCARD_IO_REQUEST* pci() { |
|
switch(_protocol) { |
|
case SCARD_PROTOCOL_T0: return SCARD_PCI_T0; |
|
case SCARD_PROTOCOL_T1: return SCARD_PCI_T1; |
|
} |
|
if (_connection->exc()) throw not_implemented("unknown protocol"); |
|
return 0; |
|
} |
|
|
|
//! Sets state and throws an exception if neccessary. |
|
/*! @throw access_error if it is instanciated for exceptions and |
|
an error occured in the last command. */ |
|
bool check(DWORD state, const std::string context="") { |
|
_state = state; |
|
return _connection->check(state, context); |
|
} |
|
|
|
//! Reconnect SmartCard After Reset |
|
/*! @param mode one of |
|
- @c SCARD_LEAVE_CARD |
|
Do not do anything special on reconnect. |
|
- @c SCARD_RESET_CARD |
|
Reset the card (Warm Reset). |
|
- @c SCARD_UNPOWER_CARD |
|
Power down the card and reset it (Cold Reset). */ |
|
void reconnect(DWORD mode=SCARD_LEAVE_CARD) { |
|
check(SCardReconnect(_id, _mode, _protocol, mode, &_protocol)); |
|
} |
|
|
|
//! Only Connection is allowed to instanciate. |
|
friend class Connection; |
|
|
|
//! Establishes a connection to the given named cardreader |
|
Reader(const std::string& nm, std::shared_ptr<Connection> c, |
|
DWORD mode=SCARD_SHARE_SHARED, |
|
DWORD protocol=SCARD_PROTOCOL_T1): |
|
name(nm), _connection(c), _mode(mode) { |
|
CRYPTOLOG("Connect Reader"); |
|
check(SCardConnect(_connection->id(), strconv(name).c_str(), |
|
_mode, protocol, |
|
&_id, &_protocol), |
|
"connect smartcard \""+name+"\""); |
|
} |
|
|
|
//! forbidden |
|
Reader(); |
|
|
|
//...........................................................variables |
|
private: |
|
|
|
std::shared_ptr<Connection> _connection; |
|
SCARDHANDLE _id; |
|
DWORD _state; |
|
DWORD _mode; |
|
DWORD _protocol; |
|
static int _neesting; |
|
|
|
}; |
|
//------------------------------------------------------------------Reader |
|
|
|
enum Scope { |
|
USER = SCARD_SCOPE_USER, |
|
TERMINAL = SCARD_SCOPE_TERMINAL, |
|
SYSTEM = SCARD_SCOPE_SYSTEM |
|
}; |
|
|
|
typedef std::vector<std::string> Strings; |
|
|
|
//................................................................methods |
|
public: |
|
|
|
//! Closes the connection (releases the smartcard context) |
|
~Connection() { |
|
CRYPTOLOG("Close Connection id="<<_id); |
|
_state = SCardReleaseContext(_id); |
|
if (!std::uncaught_exception()) check("smartcard release context"); |
|
} |
|
|
|
//! Scans for available readers from a specified list of groups. |
|
/*! Defaults to all groups. */ |
|
static Strings scan(const Strings& groups = Strings(), |
|
Scope s=USER, bool exceptions=true) { |
|
Connection c(s, exceptions); |
|
Strings res; |
|
std::string grp(c.join(groups)); |
|
DWORD num(0); |
|
if (!c.check(SCardListReaders(c._id, |
|
groups.size()?strconv(grp).data():0, 0, |
|
&num), |
|
"smartcard get size of readers of groups "+grp)) |
|
return res; |
|
CRYPTOLOG("size of readers: "<<num); |
|
if (!num) return res; |
|
std::auto_ptr<char_t> nm(new char_t[num]); |
|
if (!c.check(SCardListReaders(c._id, |
|
groups.size()?strconv(grp).data():0, |
|
nm.get(), &num), |
|
"smartcard list reader names of groups "+grp)) |
|
return res; |
|
CRYPTOLOG("got all readers, size is "<<num); |
|
if (!num) return res; |
|
CRYPTOLOG("list of readers: " |
|
<<crypto::readable(std::string(nm.get(), num-1))); |
|
return res = c.split(strconv(string(nm.get(), num-1))); |
|
} |
|
|
|
//! Find all readers with a given ATR. |
|
/*! @param atr full or partial ATR to match to the reader's ATR |
|
@param s scope of scanning |
|
@param exceptions whether exeptions should be thrown on error |
|
@returns list of readers that contain @c atr in their ATR */ |
|
static Strings getReadersWithAtr(const std::string& atr, |
|
Scope s=USER, bool exceptions=true) { |
|
CRYPTOLOG("getting all readers with atr: "<<atr); |
|
Connection c(s, exceptions); |
|
Strings res; |
|
pcsc::Connection::Strings readers(c.scan()); |
|
for (pcsc::Connection::Strings::const_iterator it(readers.begin()); |
|
it!=readers.end(); ++it) |
|
try { |
|
if (crypto::hex(c.reader(*it)->status().atr).find(atr) |
|
!=string::npos) |
|
CRYPTOLOG("found reader: "<<(*it)); |
|
res.push_back(*it); |
|
} catch (std::exception& x) { // ignore unusable readers |
|
CRYPTOLOG("ignored unusable reader: "<<x.what()); |
|
} |
|
return res; |
|
} |
|
|
|
//! Get a reader, open a connection if not already open. |
|
/*! First use scan() to get a list of readers, then open a |
|
connection to the reader, then access it. */ |
|
static std::shared_ptr<Reader> reader(const std::string& name, |
|
Scope s=USER, bool exceptions=true) { |
|
CRYPTOLOG("get reader: "<<name); |
|
return std::shared_ptr<Reader> |
|
(new Reader(name, |
|
std::shared_ptr<Connection> |
|
(new Connection(s, exceptions)))); |
|
} |
|
|
|
//................................................................methods |
|
private: |
|
|
|
//! Opens a connection (establishes a smartcard context) |
|
/*! The errorhandling is defined with the @c exceptions flag: |
|
|
|
Using exceptions: |
|
@code |
|
try { |
|
pcsc::Connection c; // standard with exceptions |
|
pcsc::Connection::Strings r(c.scan()); |
|
} catch (std::exception& x) { |
|
std::cout<<"Error with message: "<<x.what()<<std::endl; |
|
} |
|
@endcode |
|
|
|
Without exceptions: |
|
@code |
|
pcsc::Connection c(SYSTEM, false); |
|
if (!c) std::cout<<"Error with message: "<<c.error()<<std::endl; |
|
pcsc::Connection::Strings r(c.scan()); |
|
if (!c) std::cout<<"Error with message: "<<c.error()<<std::endl; |
|
@endcode |
|
|
|
Recommendation: Use exceptions! |
|
|
|
@param s defines the scope of the connection |
|
@param exceptions |
|
- @c true: class throws exceptions in case of an error |
|
- @c false: no exceptions, check your instance after each |
|
operation */ |
|
Connection(Scope s=USER, bool exceptions=true): |
|
_exc(exceptions), _id(0), _s(s), |
|
_state(SCardEstablishContext(s, 0, 0, &_id)) { |
|
CRYPTOLOG("Open Connection id="<<_id); |
|
check("establish smartcard context"); |
|
} |
|
|
|
//! Splits a buffer with 0 separators into a vector of strings. |
|
static Strings split(const std::string& in) { |
|
Strings res; |
|
for (std::string::size_type pos(0); pos<in.size() && in[pos]!=0; |
|
pos+=res.rbegin()->size()+1) |
|
res.push_back(in.substr(pos).c_str()); |
|
return res; |
|
} |
|
|
|
//! Joins a vector of strings into a buffer with 0 separators. |
|
static std::string join(const Strings& in) { |
|
std::string res; |
|
if (in.size()) { |
|
for (Strings::const_iterator it(in.begin()); |
|
it!=in.end(); ++it) |
|
res+=*it+'\0'; |
|
res+='\0'; |
|
} |
|
return res; |
|
} |
|
|
|
operator bool() const { |
|
#ifdef WIN32 |
|
return (_state>>30&3)==0; |
|
#else |
|
return _state==SCARD_S_SUCCESS; |
|
#endif |
|
} |
|
|
|
//! Throws an exception if neccessary. |
|
/*! @throw access_error if it is instanciated for exceptions and |
|
an error occured in the last command. */ |
|
bool check(DWORD s, const std::string& context="") { |
|
_state = s; |
|
return check(context); |
|
} |
|
|
|
//! Throws an exception if neccessary. |
|
/*! @throw access_error if it is instanciated for exceptions and |
|
an error occured in the last command. */ |
|
bool check(const std::string& context="") { |
|
if (_exc&&!*this) { |
|
if (context.size()) { |
|
#ifdef SCARD_W_WRONG_CHV |
|
if (_state==SCARD_W_WRONG_CHV) { |
|
throw wrong_pin(context+": "+error()); |
|
} else { |
|
#endif |
|
throw access_error(context+": "+error()); |
|
#ifdef SCARD_W_WRONG_CHV |
|
} |
|
#endif |
|
} else { |
|
#ifdef SCARD_W_WRONG_CHV |
|
if (_state==SCARD_W_WRONG_CHV) { |
|
throw wrong_pin(error()); |
|
} else { |
|
#endif |
|
throw access_error(error()); |
|
#ifdef SCARD_W_WRONG_CHV |
|
} |
|
#endif |
|
} |
|
} |
|
return *this; |
|
} |
|
|
|
//! Get the describing text of the last error |
|
std::string error() const { |
|
std::stringstream ss; |
|
switch (_state) { |
|
#ifdef SCARD_E_CANCELLED |
|
case SCARD_E_CANCELLED: |
|
ss<<"The action was canceled by an SCardCancel request."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_CANT_DISPOSE |
|
case SCARD_E_CANT_DISPOSE: |
|
ss<<"The system could not dispose of the media in the requested" |
|
<<" manner."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_CARD_UNSUPPORTED |
|
case SCARD_E_CARD_UNSUPPORTED: |
|
ss<<"The smart card does not meet minimal requirements for" |
|
<<" support."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_DUPLICATE_READER |
|
case SCARD_E_DUPLICATE_READER: |
|
ss<<"The reader driver did not produce a unique reader name."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INSUFFICIENT_BUFFER |
|
case SCARD_E_INSUFFICIENT_BUFFER: |
|
ss<<"The data buffer for returned data is too small for the" |
|
<<" returned data."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INVALID_ATR |
|
case SCARD_E_INVALID_ATR: |
|
ss<<"An ATR string obtained from the registry is not a valid" |
|
<<" ATR string."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INVALID_HANDLE |
|
case SCARD_E_INVALID_HANDLE: |
|
ss<<"The supplied handle was not valid."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INVALID_PARAMETER |
|
case SCARD_E_INVALID_PARAMETER: |
|
ss<<"One or more of the supplied parameters could not be properly" |
|
<<" interpreted."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INVALID_TARGET |
|
case SCARD_E_INVALID_TARGET: |
|
ss<<"Registry startup information is missing or not valid."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INVALID_VALUE |
|
case SCARD_E_INVALID_VALUE: |
|
ss<<"One or more of the supplied parameters values could not" |
|
<<" be properly interpreted."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NOT_READY |
|
case SCARD_E_NOT_READY: |
|
ss<<"The reader or smart card is not ready to accept commands."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NOT_TRANSACTED |
|
case SCARD_E_NOT_TRANSACTED: |
|
ss<<"An attempt was made to end a nonexistent transaction."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_MEMORY |
|
case SCARD_E_NO_MEMORY: |
|
ss<<"Not enough memory available to complete this command."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_SERVICE |
|
case SCARD_E_NO_SERVICE: |
|
ss<<"The smart card resource manager is not running."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_SMARTCARD |
|
case SCARD_E_NO_SMARTCARD: |
|
ss<<"The operation requires a smart card, but no smart card" |
|
<<" is currently in the device."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_PCI_TOO_SMALL |
|
case SCARD_E_PCI_TOO_SMALL: |
|
ss<<"The PCI receive buffer was too small."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_PROTO_MISMATCH |
|
case SCARD_E_PROTO_MISMATCH: |
|
ss<<"The requested protocols are incompatible with the protocol" |
|
<<" currently in use with the smart card."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_READER_UNAVAILABLE |
|
case SCARD_E_READER_UNAVAILABLE: |
|
ss<<"The specified reader is not currently available for use."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_READER_UNSUPPORTED |
|
case SCARD_E_READER_UNSUPPORTED: |
|
ss<<"The reader driver does not meet minimal requirements for" |
|
<<" support."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_SERVICE_STOPPED |
|
case SCARD_E_SERVICE_STOPPED: |
|
ss<<"The smart card resource manager has shut down."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_SHARING_VIOLATION |
|
case SCARD_E_SHARING_VIOLATION: |
|
ss<<"The smart card cannot be accessed because of other" |
|
<<" outstanding connections."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_SYSTEM_CANCELLED |
|
case SCARD_E_SYSTEM_CANCELLED: |
|
ss<<"The action was cancelled by the system, presumably to log" |
|
<<" off or shut down."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_TIMEOUT |
|
case SCARD_E_TIMEOUT: |
|
ss<<"The user-specified time-out value has expired."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_UNKNOWN_CARD |
|
case SCARD_E_UNKNOWN_CARD: |
|
ss<<"The specified smart card name is not recognized."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_UNKNOWN_READER |
|
case SCARD_E_UNKNOWN_READER: |
|
ss<<"The specified reader name is not recognized."; |
|
break; |
|
#endif |
|
#ifdef SCARD_F_COMM_ERROR |
|
case SCARD_F_COMM_ERROR: |
|
ss<<"An internal communications error has been detected."; |
|
break; |
|
#endif |
|
#ifdef SCARD_F_INTERNAL_ERROR |
|
case SCARD_F_INTERNAL_ERROR: |
|
ss<<"An internal consistency check failed."; |
|
break; |
|
#endif |
|
#ifdef SCARD_F_UNKNOWN_ERROR |
|
case SCARD_F_UNKNOWN_ERROR: |
|
ss<<"An internal error has been detected, but the source is" |
|
<<" unknown."; |
|
break; |
|
#endif |
|
#ifdef SCARD_F_WAITED_TOO_LONG |
|
case SCARD_F_WAITED_TOO_LONG: |
|
ss<<"An internal consistency timer has expired."; |
|
break; |
|
#endif |
|
#ifdef SCARD_S_SUCCESS |
|
case SCARD_S_SUCCESS: |
|
ss<<"No error was encountered."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_REMOVED_CARD |
|
case SCARD_W_REMOVED_CARD: |
|
ss<<"The smart card has been removed, so that further" |
|
<<" communication is not possible."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_RESET_CARD |
|
case SCARD_W_RESET_CARD: |
|
ss<<"The smart card was reset."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_UNPOWERED_CARD |
|
case SCARD_W_UNPOWERED_CARD: |
|
ss<<"Power has been removed from the smart card, so that" |
|
<<" further communication is not possible."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_UNRESPONSIVE_CARD |
|
case SCARD_W_UNRESPONSIVE_CARD: |
|
ss<<"The smart card is not responding to a reset."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_UNSUPPORTED_CARD |
|
case SCARD_W_UNSUPPORTED_CARD: |
|
ss<<"The reader cannot communicate with the smart card," |
|
<<" due to ATR configuration conflicts."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_READERS_AVAILABLE |
|
case SCARD_E_NO_READERS_AVAILABLE: |
|
ss<<"No smart card reader is available."; |
|
break; |
|
#endif |
|
#ifdef ERROR_BROKEN_PIPE |
|
case ERROR_BROKEN_PIPE: |
|
ss<<"The client attempted a smart card operation in a" |
|
<<" remote session, such as a client session running" |
|
<<" on a terminal server, and the operating system in" |
|
<<" use does not support smart card redirection."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_BAD_SEEK |
|
case SCARD_E_BAD_SEEK: |
|
ss<<"There was an error trying to set the smart card file" |
|
<<" object pointer."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_CERTIFICATE_UNAVAILABLE |
|
case SCARD_E_CERTIFICATE_UNAVAILABLE: |
|
ss<<"The requested certificate could not be obtained."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_COMM_DATA_LOST |
|
case SCARD_E_COMM_DATA_LOST: |
|
ss<<"A communications error with the smart card has been detected."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_DIR_NOT_FOUND |
|
case SCARD_E_DIR_NOT_FOUND: |
|
ss<<"The specified directory does not exist in the smart card."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_FILE_NOT_FOUND |
|
case SCARD_E_FILE_NOT_FOUND: |
|
ss<<"The specified file does not exist in the smart card."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_ICC_CREATEORDER |
|
case SCARD_E_ICC_CREATEORDER: |
|
ss<<"The requested order of object creation is not supported."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_ICC_INSTALLATION |
|
case SCARD_E_ICC_INSTALLATION: |
|
ss<<"No primary provider can be found for the smart card."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_INVALID_CHV |
|
case SCARD_E_INVALID_CHV: |
|
ss<<"The supplied PIN is incorrect."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_ACCESS |
|
case SCARD_E_NO_ACCESS: |
|
ss<<"Access is denied to this file."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_DIR |
|
case SCARD_E_NO_DIR: |
|
ss<<"The supplied path does not represent a smart card directory."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_FILE |
|
case SCARD_E_NO_FILE: |
|
ss<<"The supplied path does not represent a smart card file."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_KEY_CONTAINER |
|
case SCARD_E_NO_KEY_CONTAINER: |
|
ss<<"The requested key container does not exist on the smart card."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_NO_SUCH_CERTIFICATE |
|
case SCARD_E_NO_SUCH_CERTIFICATE: |
|
ss<<"The requested certificate does not exist."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_SERVER_TOO_BUSY |
|
case SCARD_E_SERVER_TOO_BUSY: |
|
ss<<"The Smart card resource manager is too busy to complete this" |
|
<<" operation."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_UNSUPPORTED_FEATURE |
|
case SCARD_E_UNSUPPORTED_FEATURE: |
|
ss<<"This smart card does not support the requested feature."; |
|
break; |
|
#else |
|
#ifdef SCARD_E_UNEXPECTED |
|
case SCARD_E_UNEXPECTED: |
|
ss<<"An unexpected card error has occurred."; |
|
break; |
|
#endif |
|
#endif |
|
#ifdef SCARD_E_UNKNOWN_RES_MNG |
|
case SCARD_E_UNKNOWN_RES_MNG: |
|
ss<<"An unrecognized error code was returned from a layered" |
|
<<" component."; |
|
break; |
|
#endif |
|
#ifdef SCARD_E_WRITE_TOO_MANY |
|
case SCARD_E_WRITE_TOO_MANY: |
|
ss<<"The smartcard does not have enough memory to store the" |
|
<<" information."; |
|
break; |
|
#endif |
|
#ifdef SCARD_P_SHUTDOWN |
|
case SCARD_P_SHUTDOWN: |
|
ss<<"The operation has been aborted to allow the server application" |
|
<<" to exit."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_CANCELLED_BY_USER |
|
case SCARD_W_CANCELLED_BY_USER: |
|
ss<<"The action was cancelled by the user."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_CARD_NOT_AUTHENTICATED |
|
case SCARD_W_CARD_NOT_AUTHENTICATED: |
|
ss<<"No PIN was presented to the smart card."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_CHV_BLOCKED |
|
case SCARD_W_CHV_BLOCKED: |
|
ss<<"The card cannot be accessed because the maximum number" |
|
<<" of PIN entry attempts has been reached."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_EOF |
|
case SCARD_W_EOF: |
|
ss<<"The end of the smart card file has been reached."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_SECURITY_VIOLATION |
|
case SCARD_W_SECURITY_VIOLATION: |
|
ss<<"Access was denied because of a security violation."; |
|
break; |
|
#endif |
|
#ifdef SCARD_W_WRONG_CHV |
|
case SCARD_W_WRONG_CHV: |
|
ss<<"The card cannot be accessed because the wrong PIN was" |
|
<<" presented."; |
|
break; |
|
#endif |
|
default: |
|
ss<<"unknown PCSC state=0x" |
|
<<std::hex<<std::setfill('0')<<std::setw(8)<<_state; |
|
switch (_state>>30) { |
|
case 0: ss<<" means SUCCESS"; break; |
|
case 1: ss<<" means INFORMATIONAL"; break; |
|
case 2: ss<<" means WARNING"; break; |
|
case 3: ss<<" means ERROR"; break; |
|
default: ss<<" illegal value"; |
|
} |
|
ss<<" C="<<(_state>>29&1); |
|
ss<<" R="<<(_state>>28&1); |
|
ss<<" Facility=0x"<<std::hex<<std::setfill('0')<<std::setw(3) |
|
<<(_state>>16&0xfff); |
|
ss<<" Code=0x"<<std::hex<<std::setfill('0')<<std::setw(4) |
|
<<(_state&0xffff); |
|
} |
|
return ss.str(); |
|
} |
|
//! set state |
|
void state(DWORD s) { |
|
_state = s; |
|
} |
|
//! @returns state |
|
DWORD state() { |
|
return _state; |
|
} |
|
//! @returns connection id |
|
SCARDCONTEXT id() { |
|
return _id; |
|
} |
|
bool exc() { |
|
return _exc; |
|
} |
|
|
|
private: |
|
|
|
bool _exc; |
|
SCARDCONTEXT _id; |
|
Scope _s; |
|
DWORD _state; |
|
|
|
}; |
|
|
|
//@} |
|
|
|
} |
|
//@} |
|
|
|
#endif
|
|
|