/*! @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
# 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
# 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 < < ' ) ' ;
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 {
public :
not_implemented ( const std : : string & reason ) throw ( ) :
exception ( " feature is not implemented: \n " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class access_error : public 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 ( ) {
DWORD dummy ( 0 ) ;
DWORD s ;
DWORD 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 ) {
DWORD 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. */
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 ( 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 , strconv ( name ) . c_str ( ) ,
SCARD_SHARE_SHARED ,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 ,
& _id , & _protocol ) ) ;
}
//! forbidden
Reader ( ) ;
//! forbidden
Reader ( const Reader & ) ;
//...........................................................variables
private :
Connection & _connection ;
SCARDHANDLE _id ;
long _state ;
DWORD _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 ) ) ;
DWORD num ( 0 ) ;
if ( ! check ( SCardListReaders ( _id , grp . size ( ) ? strconv ( grp ) . data ( ) : 0 , 0 ,
& num ) ) )
return res ;
std : : auto_ptr < char_t > nm ( new char_t [ num ] ) ;
if ( ! check ( SCardListReaders ( _id , grp . size ( ) ? strconv ( grp ) . data ( ) : 0 ,
nm . get ( ) , & num ) ) )
return res ;
return res = split ( strconv ( 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 {
# 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 ;
}
return ss . str ( ) ;
# else
return pcsc_stringify_error ( _state ) ;
# endif
}
//................................................................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 ;
SCARDCONTEXT _id ;
long _state ;
std : : map < std : : string , boost : : shared_ptr < Reader > > _reader ;
} ;
}
# endif