/*! @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 #include #include #include namespace pcsc { const std::string& strconv(const std::string& s) { return s; } typedef char char_t; typedef std::string string; } #endif #include #include #include #include #include #include #include //! 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"<& 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 attr; private: static std::vector convert(const std::string& a) { std::vector 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("disconnect smartcard"); } //! 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), "query smartcard status"); 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), "smartcard transmit message "+hex(in)); 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), "smartcard begin transaction"); } //! Use scoped transactions with @ref Transaction void commitTransaction() { check(SCardEndTransaction(_id, SCARD_LEAVE_CARD), "smartcard end transaction"); } //! Use scoped transactions with @ref Transaction void cancelTransaction() { check(SCardCancelTransaction(_id), "smartcard cancel transaction"); } /*! @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, const std::string context="") { _state = state; return _connection.check(state, context); } //! 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), "connect smartcard \""+name+"\""); } //! forbidden Reader(); //! forbidden Reader(const Reader&); //...........................................................variables private: Connection& _connection; SCARDHANDLE _id; DWORD _state; DWORD _protocol; }; //------------------------------------------------------------------Reader enum Scope { USER = SCARD_SCOPE_USER, TERMINAL = SCARD_SCOPE_TERMINAL, SYSTEM = SCARD_SCOPE_SYSTEM }; typedef std::vector 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: "< nm(new char_t[num]); if (!check(SCardListReaders(_id, grp.size()?strconv(grp).data():0, nm.get(), &num), "(2) smartcard list readers of groups "+grp)) 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_E_SHARING_VIOLATION: ss<<"The smart card cannot be accessed because of other" <<" connections outstanding."; break; case SCARD_E_NO_SMARTCARD: ss<<"The operation requires a Smart Card, but no Smart Card" <<" is currently in the device."; break; case SCARD_E_UNKNOWN_CARD: ss<<"The specified smart card name is not recognized."; break; case SCARD_E_CANT_DISPOSE: ss<<"The system could not dispose of the media in the" <<" requested manner."; break; case SCARD_E_PROTO_MISMATCH: ss<<"The requested protocols are incompatible with the" <<" protocol currently in use with the smart card."; break; case SCARD_E_NOT_READY: ss<<"The reader or smart card is not ready to accept commands."; break; case SCARD_E_INVALID_VALUE: ss<<"One or more of the supplied parameters values could" <<" not be properly interpreted."; break; case SCARD_E_SYSTEM_CANCELLED: ss<<"The action was cancelled by the system, presumably" <<" to log off or shut down."; break; case SCARD_F_COMM_ERROR: ss<<"An internal communications error has been detected."; break; case SCARD_F_UNKNOWN_ERROR: ss<<"An internal error has been detected, but the source" <<" is unknown."; break; case SCARD_E_INVALID_ATR: ss<<"An ATR obtained from the registry is not a valid ATR string."; break; case SCARD_E_NOT_TRANSACTED: ss<<"An attempt was made to end a non-existent transaction."; break; case SCARD_E_READER_UNAVAILABLE: ss<<"The specified reader is not currently available for use."; break; case SCARD_P_SHUTDOWN: ss<<"The operation has been aborted to allow the server" <<" application to exit."; break; case SCARD_E_PCI_TOO_SMALL: ss<<"The PCI Receive buffer was too small."; break; case SCARD_E_READER_UNSUPPORTED: ss<<"The reader driver does not meet minimal requirements" <<" for support."; break; case SCARD_E_DUPLICATE_READER: ss<<"The reader driver did not produce a unique reader name."; break; case SCARD_E_CARD_UNSUPPORTED: ss<<"The smart card does not meet minimal requirements" <<" for support."; break; case SCARD_E_NO_SERVICE: ss<<"The Smart card resource manager is not running."; break; case SCARD_E_SERVICE_STOPPED: ss<<"The Smart card resource manager has shut down."; break; case SCARD_E_UNEXPECTED: ss<<"An unexpected card error has occurred."; break; case SCARD_E_ICC_INSTALLATION: ss<<"No Primary Provider can be found for the smart card."; break; case SCARD_E_ICC_CREATEORDER: ss<<"The requested order of object creation is not supported."; break; case SCARD_E_UNSUPPORTED_FEATURE: ss<<"This smart card does not support the requested feature."; break; case SCARD_E_DIR_NOT_FOUND: ss<<"The identified directory does not exist in the smart card."; break; case SCARD_E_FILE_NOT_FOUND: ss<<"The identified file does not exist in the smart card."; break; case SCARD_E_NO_DIR: ss<<"The supplied path does not represent a smart card directory."; break; case SCARD_E_NO_FILE: ss<<"The supplied path does not represent a smart card file."; break; case SCARD_E_NO_ACCESS: ss<<"Access is denied to this file."; break; case SCARD_E_WRITE_TOO_MANY: ss<<"The smartcard does not have enough memory to store" <<" the information."; break; case SCARD_E_BAD_SEEK: ss<<"There was an error trying to set the smart card file" <<" object pointer."; break; case SCARD_E_INVALID_CHV: ss<<"The supplied PIN is incorrect."; break; case SCARD_E_UNKNOWN_RES_MNG: ss<<"An unrecognized error code was returned from a layered" <<" component."; break; case SCARD_E_NO_SUCH_CERTIFICATE: ss<<"The requested certificate does not exist."; break; case SCARD_E_CERTIFICATE_UNAVAILABLE: ss<<"The requested certificate could not be obtained."; break; case SCARD_E_NO_READERS_AVAILABLE: ss<<"Cannot find a smart card reader."; break; case SCARD_E_COMM_DATA_LOST: ss<<"A communications error with the smart card has been" <<" detected. Retry the operation."; break; case SCARD_E_NO_KEY_CONTAINER: ss<<"The requested key container does not exist on the" <<" smart card."; break; case SCARD_E_SERVER_TOO_BUSY: ss<<"The Smart card resource manager is too busy to complete" <<" this operation."; break; case SCARD_W_UNSUPPORTED_CARD: ss<<"The reader cannot communicate with the smart card, due" <<" to ATR configuration conflicts."; 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: "<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 > _reader; }; } #endif