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.

406 lines
14 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
#include <PCSC/wintypes.h>
#include <PCSC/pcsclite.h>
#include <PCSC/winscard.h>
#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<<std::hex<<std::setfill('0')<<std::setw(2)<<(int)*it;
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: exception {
public:
not_implemented(const std::string& reason) throw():
exception("feature is not implemented:\n"+reason) {
}
};
//----------------------------------------------------------------------------
class access_error: exception {
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() {
unsigned long dummy(0);
unsigned long s;
unsigned long len(MAX_ATR_SIZE);
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) {
unsigned long len(1024); // arbitrary
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. */
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(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) {
check(SCardConnect(_connection._id, name.c_str(),
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
&_id, &_protocol));
}
//! forbidden
Reader();
//! forbidden
Reader(const Reader&);
//...........................................................variables
private:
Connection& _connection;
long _id;
long _state;
unsigned long _protocol;
};
//------------------------------------------------------------------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));
unsigned long num(0);
if (!check(SCardListReaders(_id, grp.size()?grp.data():0, 0, &num)))
return res;
std::auto_ptr<char> nm(new char[num]);
if (!check(SCardListReaders(_id, grp.size()?grp.data():0,
nm.get(), &num)))
return res;
return res = split(std::string(nm.get(), num-1));
}
//! 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 {
return pcsc_stringify_error(_state);
}
//................................................................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;
long _id;
long _state;
std::map<std::string, boost::shared_ptr<Reader> > _reader;
};
}
#endif