Friday, July 10, 2009

Smart Cards Hurt - 2

Now, the slightly harder part is communicating with the Smart Card reader. Most, if not all, of the functionality resides within the winscard.dll. For functions reference, this MSDN page could be a start.

Smart Card Functions

I also found a nice example using google code search which resides here

ACR120Driver.cs

and using this code as a template, I used the following code to test the functionality of my SCM reader.

long retCode;
int hContext = 0;
int ReaderCount = 0;
int Protocol = 0;
int hCard = 0;
string defaultReader = null;
int SendLen, RecvLen;

byte[] SendBuff = new byte[262];
byte[] RecvBuff = new byte[262];

ModWinsCard.SCARD_IO_REQUEST ioRequest;

retCode = ModWinsCard.SCardEstablishContext(ModWinsCard.SCARD_SCOPE_USER, 0, 0, ref hContext);
if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
System.Diagnostics.Debug.WriteLine(ModWinsCard.GetScardErrMsg(retCode));
}

retCode = ModWinsCard.SCardListReaders(hContext, null, null, ref ReaderCount);

if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
System.Diagnostics.Debug.WriteLine(ModWinsCard.GetScardErrMsg(retCode));
}

byte[] retData = new byte[ReaderCount];
byte[] sReaderGroup = new byte[0];

//Get the list of reader present again but this time add sReaderGroup, retData as 2rd & 3rd parameter respectively.
retCode = ModWinsCard.SCardListReaders(hContext, sReaderGroup, retData, ref ReaderCount);

if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
System.Diagnostics.Debug.WriteLine(ModWinsCard.GetScardErrMsg(retCode));
}

//Convert retData(Hexadecimal) value to String
string readerStr = System.Text.ASCIIEncoding.ASCII.GetString(retData);
string[] rList = readerStr.Split('\0');

foreach (string readerName in rList)
{
if (readerName != null && readerName.Length > 1)
{
defaultReader = readerName;
break;
}
}

if (defaultReader != null)
{
retCode = ModWinsCard.SCardConnect(hContext, defaultReader, ModWinsCard.SCARD_SHARE_DIRECT,
ModWinsCard.SCARD_PROTOCOL_UNDEFINED, ref hCard, ref Protocol);
//Check if it connects successfully
if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
string error = ModWinsCard.GetScardErrMsg(retCode);
}
else
{
int pcchReaderLen = 256;
int state = 0;
byte atr = 0;
int atrLen = 255;

//get card status
retCode = ModWinsCard.SCardStatus(hCard, defaultReader, ref pcchReaderLen, ref state, ref Protocol, ref atr, ref atrLen);

if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
return;
}

//read/write data etc.

.....
}
}

ModWinsCard.cs is, again, a wrapper for the winscard.dll functions, data structures, and declares all required constants.

Anyway, this code actually worked fine, except one little detail - the state variable that gets returned by the SCardStatus returned the value of 2. And the possible values are explained here:

SCardStatus

"2" is SCARD_PRESENT, which means "A card is present in the card reader, but it is not in position for use". A better result would be something like SCARD_NEGOTIABLE which is "The card has been reset and is waiting for protocol negotiation".

Also, using SCardConnect with preferred protocol set to T0 or T1 returned SCARD_W_UNRESPONSIVE_CARD error.

Now this is the point where I had to consult with the printer manufacturer because there's a number of possible reasons for the errors - hardware, firmware, drivers or incompatible card. Work still in progress.

by . Also posted on my website

No comments: