#include "pcsc++.h"
#include <security_utilities/debugging.h>
#include <PCSC/pcsclite.h>
#include <PCSC/wintypes.h>
#include <Security/cssmapple.h>
namespace Security {
namespace PCSC {
static void decode(vector<string> &names, const char *buffer, size_t size)
{
names.clear();
for (size_t pos = 0; pos < size - 1; ) {
size_t len = strlen(buffer + pos);
names.push_back(string(buffer + pos, len));
pos += len + 1;
}
}
inline void decode(vector<string> &names, const vector<char> &buffer, size_t size)
{
decode(names, &buffer[0], size);
}
Error::Error(unsigned long err) : error(err)
{
SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err);
}
const char *Error::what() const throw ()
{
return pcsc_stringify_error((int32_t)error);
}
void Error::throwMe(unsigned long err)
{
throw Error(err);
}
OSStatus Error::osStatus() const
{
switch (error)
{
case SCARD_W_RESET_CARD:
return CSSMERR_CSP_DEVICE_RESET;
default:
return CSSMERR_CSP_INTERNAL_ERROR;
}
}
int Error::unixError() const
{
return EINVAL; }
void ReaderState::set(const char *name, unsigned long known)
{
clearPod();
szReader = name;
pvUserData = NULL;
dwCurrentState = (uint32_t)known;
}
void ReaderState::lastKnown(unsigned long s)
{
dwCurrentState = (uint32_t)s & ~(SCARD_STATE_CHANGED | SCARD_STATE_UNAVAILABLE);
}
void ReaderState::setATR(const void *atr, size_t size)
{
if (size > sizeof(rgbAtr))
Error::throwMe(SCARD_E_INVALID_ATR);
memcpy(rgbAtr, atr, size);
cbAtr = (uint32_t)size;
}
#if defined(DEBUGDUMP)
void ReaderState::dump()
{
Debug::dump("reader(%s) state=0x%x events=0x%x",
szReader ? szReader : "(null)", dwCurrentState, dwEventState);
Debug::dumpData(" ATR", rgbAtr, cbAtr);
}
#endif //DEBUGDUMP
Session::Session()
: mIsOpen(false)
{
}
Session::~Session()
{
close();
}
void Session::open()
{
if (!mIsOpen) {
try {
Error::check(::SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &mContext));
mIsOpen = true;
secdebug("pcsc", "context opened");
} catch (const Error &err) {
if (err.error == SCARD_F_INTERNAL_ERROR)
{
secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready");
return;
}
}
}
}
void Session::close()
{
if (mIsOpen) {
mIsOpen = false;
try {
if (mContext)
Error::check(SCardReleaseContext(mContext));
secdebug("pcsc", "context closed");
} catch (const Error &err) {
if (err.error == SCARD_F_INTERNAL_ERROR)
{
secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready");
return;
}
}
}
}
bool Session::check(long rc)
{
switch (rc) {
case SCARD_S_SUCCESS:
return true; case SCARD_E_READER_UNAVAILABLE:
return false; default:
Error::throwMe(rc);
return false; }
}
void Session::listReaders(vector<string> &readers, const char *groups)
{
uint32_t size = 0;
if (check(::SCardListReaders(mContext, groups, NULL, &size)))
{
mReaderBuffer.resize(size);
if (check(::SCardListReaders(mContext, groups, &mReaderBuffer[0], &size)))
{
decode(readers, mReaderBuffer, size);
return;
}
}
readers.clear(); }
void Session::statusChange(ReaderState *readers, unsigned int nReaders, long timeout)
{
if (nReaders == 0)
return; check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders));
}
Card::Card()
: mConnectedState(kInitial)
{
}
Card::~Card()
{
}
void Card::setIOType(unsigned long activeProtocol)
{
switch (activeProtocol)
{
case SCARD_PROTOCOL_T0:
mIOType = SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
mIOType = SCARD_PCI_T1;
break;
default:
mIOType = SCARD_PCI_RAW;
break;
}
}
void Card::connect(Session &session, const char *reader,
unsigned long share, unsigned long protocols)
{
uint32_t activeProtocol;
Error::check(::SCardConnect(session.mContext,
reader, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol));
setIOType(activeProtocol);
mConnectedState = kConnected;
}
void Card::reconnect(unsigned long share, unsigned long protocols, unsigned long initialization)
{
assert(mConnectedState != kInitial);
uint32_t activeProtocol;
Error::check(::SCardReconnect(mHandle, (uint32_t)share, (uint32_t)protocols,
(uint32_t)initialization, &activeProtocol));
setIOType(activeProtocol);
mConnectedState = kConnected;
}
void Card::disconnect(unsigned long disposition)
{
if (mConnectedState == kConnected)
{
if (mTransactionNestLevel > 0)
{
secdebug("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel);
mTransactionNestLevel = 0;
}
checkReset(::SCardDisconnect(mHandle, (uint32_t)disposition));
didDisconnect();
mConnectedState = kInitial;
}
}
void
Card::checkReset(unsigned int rv)
{
if (rv == SCARD_W_RESET_CARD)
{
secdebug("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
didDisconnect();
}
Error::check(rv);
}
void
Card::didDisconnect()
{
mConnectedState = kDisconnected;
mTransactionNestLevel = 0;
}
void
Card::didEnd()
{
}
void
Card::transmit(const unsigned char *pbSendBuffer, size_t cbSendLength,
unsigned char *pbRecvBuffer, size_t &pcbRecvLength)
{
if (mConnectedState == kDisconnected)
{
secdebug("pcsc", "%p: transmit after disconnect, reconnecting", this);
reconnect();
}
IFDUMPING("pcsc", dump("->", pbSendBuffer, cbSendLength));
uint32_t tmpRecvLength = (uint32_t)pcbRecvLength;
checkReset(::SCardTransmit(mHandle, mIOType, pbSendBuffer, (uint32_t)cbSendLength,
NULL, pbRecvBuffer, &tmpRecvLength));
pcbRecvLength = tmpRecvLength;
IFDUMPING("pcsc", dump("<-", pbRecvBuffer, pcbRecvLength));
}
void Card::begin()
{
if (mTransactionNestLevel == 0)
{
if (mConnectedState == kDisconnected)
{
secdebug("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
reconnect();
}
checkReset(::SCardBeginTransaction(mHandle));
}
mTransactionNestLevel++;
secdebug("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel);
}
void Card::end(unsigned long disposition)
{
secdebug("pcsc", "%p end transaction: %d", this, mTransactionNestLevel);
if (disposition == SCARD_RESET_CARD)
{
if (mConnectedState == kDisconnected)
{
secdebug("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
reconnect();
}
checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
didDisconnect();
}
else if (mTransactionNestLevel > 0)
{
--mTransactionNestLevel;
if (mTransactionNestLevel == 0)
{
if (mConnectedState == kDisconnected)
secdebug("pcsc", "%p: end transaction while disconnected ignored", this);
else
{
checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
didEnd();
}
}
}
}
void Card::cancel()
{
end();
}
#if defined(DEBUGDUMP)
void
Card::dump(const char *direction, const unsigned char *buffer, size_t length)
{
Debug::dump("[%02lu]%s:", length, direction);
for (size_t ix = 0; ix < length; ++ix)
Debug::dump(" %02x", buffer[ix]);
Debug::dump("\n");
}
#endif
void Transaction::commitAction()
{
mCarrier.end(mDisposition);
}
} }