using System; using System.Runtime.InteropServices; using System.Text; using System.Threading; namespace GemCard { /// /// CARD_STATE enumeration, used by the PC/SC function SCardGetStatusChanged /// enum CARD_STATE { UNAWARE = 0x00000000, IGNORE = 0x00000001, CHANGED = 0x00000002, UNKNOWN = 0x00000004, UNAVAILABLE = 0x00000008, EMPTY = 0x00000010, PRESENT = 0x00000020, ATRMATCH = 0x00000040, EXCLUSIVE = 0x00000080, INUSE = 0x00000100, MUTE = 0x00000200, UNPOWERED = 0x00000400 } /// /// Wraps the SCARD_IO_STRUCTURE /// /// [StructLayout(LayoutKind.Sequential)] public struct SCard_IO_Request { public UInt32 m_dwProtocol; public UInt32 m_cbPciLength; } /// /// Wraps theSCARD_READERSTATE structure of PC/SC /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct SCard_ReaderState { public string m_szReader; public IntPtr m_pvUserData; public UInt32 m_dwCurrentState; public UInt32 m_dwEventState; public UInt32 m_cbAtr; [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] public byte[] m_rgbAtr; } /// /// Implementation of ICard using native (P/Invoke) interoperability for PC/SC /// public class CardNative : CardBase { private UInt32 m_hContext = 0; private UInt32 m_hCard = 0; private UInt32 m_nProtocol = (uint) PROTOCOL.T0; private int m_nLastError = 0; #region PCSC_FUNCTIONS /// /// Native SCardGetStatusChanged from winscard.dll /// /// /// /// /// /// [DllImport("winscard.dll", SetLastError = true)] internal static extern int SCardGetStatusChange(UInt32 hContext, UInt32 dwTimeout, [In,Out] SCard_ReaderState[] rgReaderStates, UInt32 cReaders); /// /// Native SCardListReaders function from winscard.dll /// /// /// /// /// /// [DllImport("winscard.dll", SetLastError=true)] internal static extern int SCardListReaders(UInt32 hContext, [MarshalAs(UnmanagedType.LPTStr)] string mszGroups, IntPtr mszReaders, out UInt32 pcchReaders); /// /// Native SCardEstablishContext function from winscard.dll /// /// /// /// /// /// [DllImport("winscard.dll", SetLastError=true)] internal static extern int SCardEstablishContext(UInt32 dwScope, IntPtr pvReserved1, IntPtr pvReserved2, IntPtr phContext); /// /// Native SCardReleaseContext function from winscard.dll /// /// /// [DllImport("winscard.dll", SetLastError=true)] internal static extern int SCardReleaseContext(UInt32 hContext); /// /// Native SCardConnect function from winscard.dll /// /// /// /// /// /// /// /// [DllImport("winscard.dll", SetLastError=true, CharSet=CharSet.Auto)] internal static extern int SCardConnect(UInt32 hContext, [MarshalAs(UnmanagedType.LPTStr)] string szReader, UInt32 dwShareMode, UInt32 dwPreferredProtocols, IntPtr phCard, IntPtr pdwActiveProtocol); /// /// Native SCardDisconnect function from winscard.dll /// /// /// /// [DllImport("winscard.dll", SetLastError=true)] internal static extern int SCardDisconnect(UInt32 hCard, UInt32 dwDisposition); /// /// Native SCardTransmit function from winscard.dll /// /// /// /// /// /// /// /// /// [DllImport("winscard.dll", SetLastError=true)] internal static extern int SCardTransmit(UInt32 hCard, [In] ref SCard_IO_Request pioSendPci, byte[] pbSendBuffer, UInt32 cbSendLength, IntPtr pioRecvPci, [Out] byte[] pbRecvBuffer, out UInt32 pcbRecvLength ); /// /// Native SCardBeginTransaction function of winscard.dll /// /// /// [DllImport("winscard.dll", SetLastError = true)] internal static extern int SCardBeginTransaction(UInt32 hContext); /// /// Native SCardEndTransaction function of winscard.dll /// /// /// [DllImport("winscard.dll", SetLastError = true)] internal static extern int SCardEndTransaction(UInt32 hContext, UInt32 dwDisposition); [DllImport("winscard.dll", SetLastError = true)] internal static extern int SCardGetAttrib(UInt32 hCard, UInt32 dwAttribId, [Out] byte[] pbAttr, out UInt32 pcbAttrLen); #endregion /// /// Default constructor /// public CardNative() { } /// /// Object destruction /// ~CardNative() { Disconnect(DISCONNECT.Unpower); try { ReleaseContext(); } catch { } } #region ICard Members /// /// Wraps the PCSC function /// LONG SCardListReaders(SCARDCONTEXT hContext, /// LPCTSTR mszGroups, /// LPTSTR mszReaders, /// LPDWORD pcchReaders /// ); /// /// A string array of the readers public override string[] ListReaders() { try { EstablishContext(SCOPE.User); string[] sListReaders = null; UInt32 pchReaders = 0; IntPtr szListReaders = IntPtr.Zero; m_nLastError = SCardListReaders(m_hContext, null, szListReaders, out pchReaders); if (m_nLastError == 0) { szListReaders = Marshal.AllocHGlobal((int)pchReaders); m_nLastError = SCardListReaders(m_hContext, null, szListReaders, out pchReaders); if (m_nLastError == 0) { char[] caReadersData = new char[pchReaders]; int nbReaders = 0; for (int nI = 0; nI < pchReaders; nI++) { caReadersData[nI] = (char)Marshal.ReadByte(szListReaders, nI); if (caReadersData[nI] == 0) nbReaders++; } // Remove last 0 --nbReaders; if (nbReaders != 0) { sListReaders = new string[nbReaders]; char[] caReader = new char[pchReaders]; int nIdx = 0; int nIdy = 0; int nIdz = 0; // Get the nJ string from the multi-string while (nIdx < pchReaders - 1) { caReader[nIdy] = caReadersData[nIdx]; if (caReader[nIdy] == 0) { sListReaders[nIdz] = new string(caReader, 0, nIdy); ++nIdz; nIdy = 0; caReader = new char[pchReaders]; } else ++nIdy; ++nIdx; } } } Marshal.FreeHGlobal(szListReaders); } ReleaseContext(); return sListReaders; } catch { return new string[0]; } } /// /// Wraps the PCSC function /// LONG SCardEstablishContext( /// IN DWORD dwScope, /// IN LPCVOID pvReserved1, /// IN LPCVOID pvReserved2, /// OUT LPSCARDCONTEXT phContext /// ); /// /// public void EstablishContext(SCOPE Scope) { IntPtr hContext = Marshal.AllocHGlobal(Marshal.SizeOf(m_hContext)); m_nLastError = SCardEstablishContext((uint) Scope, IntPtr.Zero, IntPtr.Zero, hContext); if (m_nLastError != 0) { string msg = "SCardEstablishContext error: " + m_nLastError; Marshal.FreeHGlobal(hContext); throw new Exception(msg); } m_hContext = (uint) Marshal.ReadInt32(hContext); Marshal.FreeHGlobal(hContext); } /// /// Wraps the PCSC function /// LONG SCardReleaseContext( /// IN SCARDCONTEXT hContext /// ); /// public void ReleaseContext() { if (m_hContext != 0) { m_nLastError = SCardReleaseContext(m_hContext); if (m_nLastError != 0) { string msg = "SCardReleaseContext error: " + m_nLastError; throw new Exception(msg); } m_hContext = 0; } } /// /// Wraps the PCSC function /// LONG SCardConnect( /// IN SCARDCONTEXT hContext, /// IN LPCTSTR szReader, /// IN DWORD dwShareMode, /// IN DWORD dwPreferredProtocols, /// OUT LPSCARDHANDLE phCard, /// OUT LPDWORD pdwActiveProtocol /// ); /// /// /// /// public override void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols) { EstablishContext(SCOPE.User); IntPtr hCard = Marshal.AllocHGlobal(Marshal.SizeOf(m_hCard)); IntPtr pProtocol = Marshal.AllocHGlobal(Marshal.SizeOf(m_nProtocol)); m_nLastError = SCardConnect(m_hContext, Reader, (uint) ShareMode, (uint) PreferredProtocols, hCard, pProtocol); if (m_nLastError != 0) { string msg = "SCardConnect error: " + m_nLastError; Marshal.FreeHGlobal(hCard); Marshal.FreeHGlobal(pProtocol); throw new Exception(msg); } m_hCard = (uint) Marshal.ReadInt32(hCard); m_nProtocol = (uint) Marshal.ReadInt32(pProtocol); Marshal.FreeHGlobal(hCard); Marshal.FreeHGlobal(pProtocol); } /// /// Wraps the PCSC function /// LONG SCardDisconnect( /// IN SCARDHANDLE hCard, /// IN DWORD dwDisposition /// ); /// /// public override void Disconnect(DISCONNECT Disposition) { if (m_hCard != 0) { m_nLastError = SCardDisconnect(m_hCard, (uint) Disposition); m_hCard = 0; if (m_nLastError != 0) { string msg = "SCardDisconnect error: " + m_nLastError; throw new Exception(msg); } ReleaseContext(); } } /// /// Wraps the PCSC function /// LONG SCardTransmit( /// SCARDHANDLE hCard, /// LPCSCARD_I0_REQUEST pioSendPci, /// LPCBYTE pbSendBuffer, /// DWORD cbSendLength, /// LPSCARD_IO_REQUEST pioRecvPci, /// LPBYTE pbRecvBuffer, /// LPDWORD pcbRecvLength /// ); /// /// APDUCommand object with the APDU to send to the card /// An APDUResponse object with the response from the card public override APDUResponse Transmit(APDUCommand ApduCmd) { uint RecvLength = (uint) (ApduCmd.Le + APDUResponse.SW_LENGTH); byte[] ApduBuffer = null; byte[] ApduResponse = new byte[ApduCmd.Le + APDUResponse.SW_LENGTH]; SCard_IO_Request ioRequest = new SCard_IO_Request(); ioRequest.m_dwProtocol = m_nProtocol; ioRequest.m_cbPciLength = 8; // Build the command APDU if (ApduCmd.Data == null) { ApduBuffer = new byte[APDUCommand.APDU_MIN_LENGTH + ((ApduCmd.Le != 0) ? 1 : 0)]; if (ApduCmd.Le != 0) ApduBuffer[4] = (byte) ApduCmd.Le; } else { ApduBuffer = new byte[APDUCommand.APDU_MIN_LENGTH + 1 + ApduCmd.Data.Length]; for (int nI = 0; nI < ApduCmd.Data.Length; nI++) ApduBuffer[APDUCommand.APDU_MIN_LENGTH + 1 + nI] = ApduCmd.Data[nI]; ApduBuffer[APDUCommand.APDU_MIN_LENGTH] = (byte) ApduCmd.Data.Length; } ApduBuffer[0] = ApduCmd.Class; ApduBuffer[1] = ApduCmd.Ins; ApduBuffer[2] = ApduCmd.P1; ApduBuffer[3] = ApduCmd.P2; m_nLastError = SCardTransmit(m_hCard, ref ioRequest, ApduBuffer, (uint) ApduBuffer.Length, IntPtr.Zero, ApduResponse, out RecvLength); if (m_nLastError != 0) { string msg = "SCardTransmit error: " + m_nLastError; throw new Exception(msg); } byte[] ApduData = new byte[RecvLength]; for (int nI = 0; nI < RecvLength; nI++) ApduData[nI] = ApduResponse[nI]; return new APDUResponse(ApduData); } /// /// Wraps the PSCS function /// LONG SCardBeginTransaction( /// SCARDHANDLE hCard // ); /// public override void BeginTransaction() { if (m_hCard != 0) { m_nLastError = SCardBeginTransaction(m_hCard); if (m_nLastError != 0) { string msg = "SCardBeginTransaction error: " + m_nLastError; throw new Exception(msg); } } } /// /// Wraps the PCSC function /// LONG SCardEndTransaction( /// SCARDHANDLE hCard, /// DWORD dwDisposition /// ); /// /// A value from DISCONNECT enum public override void EndTransaction(DISCONNECT Disposition) { if (m_hCard != 0) { m_nLastError = SCardEndTransaction(m_hCard, (UInt32)Disposition); if (m_nLastError != 0) { string msg = "SCardEndTransaction error: " + m_nLastError; throw new Exception(msg); } } } /// /// Gets the attributes of the card /// /// Identifier for the Attribute to get /// Attribute content public override byte[] GetAttribute(UInt32 AttribId) { byte[] attr = null; UInt32 attrLen = 0; m_nLastError = SCardGetAttrib(m_hCard, AttribId, attr, out attrLen); if (m_nLastError == 0) { if (attrLen != 0) { attr = new byte[attrLen]; m_nLastError = SCardGetAttrib(m_hCard, AttribId, attr, out attrLen); if (m_nLastError != 0) { string msg = "SCardGetAttr error: " + m_nLastError; throw new Exception(msg); } } } else { string msg = "SCardGetAttr error: " + m_nLastError; throw new Exception(msg); } return attr; } #endregion /// /// This function must implement a card detection mechanism. /// /// When card insertion is detected, it must call the method CardInserted() /// When card removal is detected, it must call the method CardRemoved() /// /// protected override void RunCardDetection(object Reader) { bool bFirstLoop = true; UInt32 hContext = 0; // Local context IntPtr phContext; phContext = Marshal.AllocHGlobal(Marshal.SizeOf(hContext)); if (SCardEstablishContext((uint) SCOPE.User, IntPtr.Zero, IntPtr.Zero, phContext) == 0) { hContext = (uint)Marshal.ReadInt32(phContext); Marshal.FreeHGlobal(phContext); UInt32 nbReaders = 1; SCard_ReaderState[] readerState = new SCard_ReaderState[nbReaders]; readerState[0].m_dwCurrentState = (UInt32) CARD_STATE.UNAWARE; readerState[0].m_szReader = (string)Reader; UInt32 eventState; UInt32 currentState = readerState[0].m_dwCurrentState; // Card detection loop do { if (SCardGetStatusChange(hContext, WAIT_TIME , readerState, nbReaders) == 0) { eventState = readerState[0].m_dwEventState; currentState = readerState[0].m_dwCurrentState; // Check state if (((eventState & (uint) CARD_STATE.CHANGED) == (uint) CARD_STATE.CHANGED) && !bFirstLoop) { // State has changed if ((eventState & (uint) CARD_STATE.EMPTY) == (uint) CARD_STATE.EMPTY) { // There is no card, card has been removed -> Fire CardRemoved event CardRemoved(this, new CardRemovedEventArgs()); } if (((eventState & (uint)CARD_STATE.PRESENT) == (uint)CARD_STATE.PRESENT) && ((eventState & (uint) CARD_STATE.PRESENT) != (currentState & (uint) CARD_STATE.PRESENT))) { // There is a card in the reader -> Fire CardInserted event CardInserted(this, new CardInsertedEventArgs()); } if ((eventState & (uint) CARD_STATE.ATRMATCH) == (uint) CARD_STATE.ATRMATCH) { // There is a card in the reader and it matches the ATR we were expecting-> Fire CardInserted event CardInserted(this, new CardInsertedEventArgs()); } } // The current stateis now the event state readerState[0].m_dwCurrentState = eventState; bFirstLoop = false; } Thread.Sleep(100); if (m_bRunCardDetection == false) break; } while (true); // Exit on request } else { Marshal.FreeHGlobal(phContext); throw new Exception("PC/SC error"); } SCardReleaseContext(hContext); } } }