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.

481 lines
16 KiB

16 years ago
/*! @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
15 years ago
#ifdef WIN32
#include "WinSCard.h"
#ifndef MAX_ATR_SIZE
#define MAX_ATR_SIZE 33
#endif
namespace pcsc {
//! stupid windows needs std::wstring
std::wstring strconv(std::string s) {
return std::wstring(s.begin(), s.end());
}
std::string strconv(std::wstring s) {
return std::string(s.begin(), s.end());
}
typedef wchar_t char_t;
typedef std::wstring string;
}
#else
#include <PCSC/pcsclite.h>
#include <PCSC/wintypes.h>
#include <PCSC/pcsclite.h>
#include <PCSC/winscard.h>
namespace pcsc {
const std::string& strconv(const std::string& s) {
return s;
}
typedef char char_t;
typedef std::string string;
}
#endif
16 years ago
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <sstream>
#include <iomanip>
//! 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);
- SCardReconnect(hCard, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
SCARD_LEAVE_CARD,
&dwActiveProtocol);
- 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. */
namespace pcsc {
std::string hex(const std::string& data) {
std::stringstream res;
for (std::string::const_iterator it(data.begin()); it!=data.end(); ++it)
res<<"(0x"<<std::hex<<std::setfill('0')<<std::setw(2)
<<(unsigned int)(unsigned char)*it<<')';
16 years ago
return res.str();
}
//============================================================================
class exception: public std::exception {
public:
exception(const std::string& reason) throw(): _what("pcsc: "+reason) {}
~exception() throw() {}
const char* what() const throw() {
return _what.c_str();
}
private:
std::string _what;
};
//----------------------------------------------------------------------------
class not_implemented: public exception {
16 years ago
public:
not_implemented(const std::string& reason) throw():
exception("feature is not implemented:\n"+reason) {
}
};
//----------------------------------------------------------------------------
class access_error: public exception {
16 years ago
public:
access_error(const std::string& reason) throw():
exception("smardcard access error:\n"+reason) {
}
};
//============================================================================
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 cancelled
in the destructor, unless it is committed before. That
means: If you commit the transaction, it is executed. If
you leave the code block unexpectedly, i.e. through an
exception, the transaction is cancelled.
@code
pcsc::Connection c;
{
Transaction t(c.reader("name"));
[...] // do some stuff, possible exceptions thrown
if (problem) return; // automatically cancelled
[...]
t.commit(); // commit if we reach this line
} // cancelled, 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(Reader& r): _reader(r), _running(true) {
_reader.beginTransaction();
}
//! Cancels the transaction if not yet finished.
~Transaction() try {
if (_running) cancel();
} catch (...) {
if (!std::uncaught_exception()) throw;
}
//! Ends (commits) the running transaction.
void commit() {
if (_running) _reader.commitTransaction();
_running = false;
}
//! Cancels the running transaction.
void cancel() {
if (_running) _reader.cancelTransaction();
_running = false;
}
private:
Reader& _reader;
bool _running;
};
//! State and attribute list of a reader.
class Status {
public:
Status(unsigned long s, const std::vector<unsigned char>& attrs):
state(s), attr(attrs) {
}
Status(unsigned long s, const std::string& attrs):
state(s), attr(convert(attrs)) {
}
const unsigned long state;
const std::vector<unsigned char> attr;
private:
static std::vector<unsigned char> convert(const std::string& a) {
std::vector<unsigned char> res;
for (std::string::const_iterator it(a.begin());
it!=a.end(); ++it)
res.push_back(*it);
return res;
}
};
//.............................................................methods
public:
//! Disconnects connection.
~Reader() {
_state = SCardDisconnect(_id, SCARD_LEAVE_CARD);
if (!std::uncaught_exception()) _connection.check();
}
//! Get reader status.
Status status() {
15 years ago
DWORD dummy(0);
DWORD s;
DWORD len(MAX_ATR_SIZE);
16 years ago
unsigned char a[len];
check(SCardStatus(_id, 0, &dummy, &s, &_protocol, a, &len));
return Status(s, std::string((char*)a, len));
}
//! Transmit data to reader.
std::string transmit(std::string in) {
15 years ago
DWORD len(1024); // arbitrary
16 years ago
unsigned char buff[len];
SCARD_IO_REQUEST rPci;
check(SCardTransmit(_id, pci(),
(unsigned char*)in.c_str(), in.size(),
&rPci, buff, &len));
return std::string((char*)buff, len);
}
//! @c false if last operation was not successful
operator bool() const {
return _state==SCARD_S_SUCCESS;
}
//...........................................................variables
public:
const std::string name;
//.............................................................methods
private:
//! Use scoped transactions with @ref Transaction
void beginTransaction() {
check(SCardBeginTransaction(_id));
}
//! Use scoped transactions with @ref Transaction
void commitTransaction() {
check(SCardEndTransaction(_id, SCARD_LEAVE_CARD));
}
//! Use scoped transactions with @ref Transaction
void cancelTransaction() {
check(SCardCancelTransaction(_id));
}
/*! @throw not_implemented if _protocol is unknown. */
15 years ago
const SCARD_IO_REQUEST* pci() {
16 years ago
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(long state) {
_state = state;
return _connection.check(state);
}
//! Only Connection is allowed to instanciate.
friend class Connection;
//! Establishes a connection to the given named cardreader
Reader(const std::string& nm, Connection& c):
name(nm), _connection(c) {
15 years ago
check(SCardConnect(_connection._id, strconv(name).c_str(),
16 years ago
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
&_id, &_protocol));
}
//! forbidden
Reader();
//! forbidden
Reader(const Reader&);
//...........................................................variables
private:
Connection& _connection;
15 years ago
SCARDHANDLE _id;
16 years ago
long _state;
15 years ago
DWORD _protocol;
16 years ago
};
//------------------------------------------------------------------Reader
enum Scope {
USER = SCARD_SCOPE_USER,
TERMINAL = SCARD_SCOPE_TERMINAL,
SYSTEM = SCARD_SCOPE_SYSTEM
};
typedef std::vector<std::string> Strings;
//................................................................methods
public:
//! 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=SYSTEM, bool exceptions=true):
_exc(exceptions), _id(0),
_state(SCardEstablishContext(s, 0, 0, &_id)) {
check();
}
//! Closes the connection (releases the smartcard context)
~Connection() {
_state = SCardReleaseContext(_id);
if (!std::uncaught_exception()) check();
}
//! Scans for available readers from a specified list of groups.
/*! Defaults to all groups. */
Strings scan(const Strings& groups = Strings()) {
Strings res;
std::string grp(join(groups));
15 years ago
DWORD num(0);
if (!check(SCardListReaders(_id, grp.size()?strconv(grp).data():0, 0,
&num)))
16 years ago
return res;
15 years ago
std::auto_ptr<char_t> nm(new char_t[num]);
if (!check(SCardListReaders(_id, grp.size()?strconv(grp).data():0,
16 years ago
nm.get(), &num)))
return res;
15 years ago
return res = split(strconv(string(nm.get(), num-1)));
16 years ago
}
//! 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. */
Reader& reader(const std::string& name) {
if (_reader.find(name)==_reader.end())
_reader.insert(std::make_pair(name, new Reader(name, *this)));
return *_reader.find(name)->second;
}
//! Close connection of a named reader.
/*! If you access the same reader through raeder() later, the
connection will be reestablished. */
void close(const std::string& s) {
}
//! @c false if last operation was not successful
operator bool() const {
return _state==SCARD_S_SUCCESS;
}
//! Get the describing text of the last error
std::string error() const {
15 years ago
#ifdef WIN32
std::stringstream ss;
switch (_state) {
case SCARD_S_SUCCESS:
ss<<"Success";
break;
case SCARD_W_UNRESPONSIVE_CARD:
ss<<"The smart card is not responding to a reset.";
break;
case SCARD_W_UNPOWERED_CARD:
ss<<"Power has been removed from the smart card, so that further"
" communication is not possible.";
break;
case SCARD_W_RESET_CARD:
ss<<"The smart card has been reset, so any shared state"
" information is invalid.";
break;
case SCARD_W_REMOVED_CARD:
ss<<"The smart card has been removed, so that further"
" communication is not possible.";
break;
case SCARD_W_SECURITY_VIOLATION:
ss<<"Access was denied because of a security violation.";
break;
case SCARD_W_WRONG_CHV:
ss<<"The card cannot be accessed because the wrong PIN was"
" presented.";
break;
case SCARD_W_CHV_BLOCKED:
ss<<"The card cannot be accessed because the maximum number"
" of PIN entry attempts has been reached.";
break;
case SCARD_W_EOF:
ss<<"The end of the smart card file has been reached.";
break;
case SCARD_W_CANCELLED_BY_USER:
ss<<"The action was cancelled by the user.";
break;
case SCARD_W_CARD_NOT_AUTHENTICATED:
ss<<"No PIN was presented to the smart card.";
break;
default:
ss<<"Unknown PCSC state: "<<_state;
}
15 years ago
return ss.str();
#else
16 years ago
return pcsc_stringify_error(_state);
15 years ago
#endif
16 years ago
}
//................................................................methods
private:
//! 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(long state) {
_state = state;
return check();
}
//! Throws an exception if neccessary.
/*! @throw access_error if it is instanciated for exceptions and
an error occured in the last command. */
bool check() {
if (_exc&&_state!=SCARD_S_SUCCESS) throw access_error(error());
return _state==SCARD_S_SUCCESS;
}
//! Splits a buffer with 0 separators into a vector of strings.
Strings split(const std::string& in) {
Strings res;
for (std::string::size_type pos(0); pos<in.size();
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.
std::string join(const Strings& in) {
std::string res;
for (Strings::const_iterator it(in.begin());
it!=in.end(); ++it)
res+=*it+'\0';
return res;
}
//..............................................................variables
private:
bool _exc;
15 years ago
SCARDCONTEXT _id;
16 years ago
long _state;
std::map<std::string, boost::shared_ptr<Reader> > _reader;
};
}
#endif