Created
January 31, 2013 09:42
-
-
Save im-infamou5/4681713 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Collections.ObjectModel; | |
using System.Collections; | |
using System.Threading; | |
using System.Runtime.InteropServices; | |
namespace SpringCardPCSC | |
{ | |
/**c* SpringCardPCSC/SCARD | |
* | |
* NAME | |
* SCARD | |
* | |
* DESCRIPTION | |
* Static class that gives access to PC/SC functions (SCard... provided by winscard.dll) | |
* | |
**/ | |
public abstract partial class SCARD | |
{ | |
public static SCardReader DefaultReader = null; | |
public static SCardChannel DefaultCardChannel = null; | |
#region Constants for parameters and status | |
public const uint SCOPE_USER = 0; | |
public const uint SCOPE_TERMINAL = 1; | |
public const uint SCOPE_SYSTEM = 2; | |
public const uint SHARE_EXCLUSIVE = 1; | |
public const uint SHARE_SHARED = 2; | |
public const uint SHARE_DIRECT = 3; | |
public const uint PROTOCOL_T0 = 1; | |
public const uint PROTOCOL_T1 = 2; | |
public const uint PROTOCOL_RAW = 3; | |
public const uint LEAVE_CARD = 0; // Don't do anything special on close | |
public const uint RESET_CARD = 1; // Reset the card on close | |
public const uint UNPOWER_CARD = 2; // Power down the card on close | |
public const uint EJECT_CARD = 3; // Eject the card on close | |
public const uint STATE_UNAWARE = 0x00000000; | |
public const uint STATE_IGNORE = 0x00000001; | |
public const uint STATE_CHANGED = 0x00000002; | |
public const uint STATE_UNKNOWN = 0x00000004; | |
public const uint STATE_UNAVAILABLE = 0x00000008; | |
public const uint STATE_EMPTY = 0x00000010; | |
public const uint STATE_PRESENT = 0x00000020; | |
public const uint STATE_ATRMATCH = 0x00000040; | |
public const uint STATE_EXCLUSIVE = 0x00000080; | |
public const uint STATE_INUSE = 0x00000100; | |
public const uint STATE_MUTE = 0x00000200; | |
public const uint STATE_UNPOWERED = 0x00000400; | |
public const uint IOCTL_CSB6_PCSC_ESCAPE = 0x00312000; | |
public const uint IOCTL_MS_CCID_ESCAPE = 0x003136B0; | |
#endregion | |
#region Error codes | |
public const uint S_SUCCESS = 0x00000000; | |
public const uint F_INTERNAL_ERROR = 0x80100001; | |
public const uint E_CANCELLED = 0x80100002; | |
public const uint E_INVALID_HANDLE = 0x80100003; | |
public const uint E_INVALID_PARAMETER = 0x80100004; | |
public const uint E_INVALID_TARGET = 0x80100005; | |
public const uint E_NO_MEMORY = 0x80100006; | |
public const uint F_WAITED_TOO_LONG = 0x80100007; | |
public const uint E_INSUFFICIENT_BUFFER = 0x80100008; | |
public const uint E_UNKNOWN_READER = 0x80100009; | |
public const uint E_TIMEOUT = 0x8010000A; | |
public const uint E_SHARING_VIOLATION = 0x8010000B; | |
public const uint E_NO_SMARTCARD = 0x8010000C; | |
public const uint E_UNKNOWN_CARD = 0x8010000D; | |
public const uint E_CANT_DISPOSE = 0x8010000E; | |
public const uint E_PROTO_MISMATCH = 0x8010000F; | |
public const uint E_NOT_READY = 0x80100010; | |
public const uint E_INVALID_VALUE = 0x80100011; | |
public const uint E_SYSTEM_CANCELLED = 0x80100012; | |
public const uint F_COMM_ERROR = 0x80100013; | |
public const uint F_UNKNOWN_ERROR = 0x80100014; | |
public const uint E_INVALID_ATR = 0x80100015; | |
public const uint E_NOT_TRANSACTED = 0x80100016; | |
public const uint E_READER_UNAVAILABLE = 0x80100017; | |
public const uint P_SHUTDOWN = 0x80100018; | |
public const uint E_PCI_TOO_SMALL = 0x80100019; | |
public const uint E_READER_UNSUPPORTED = 0x8010001A; | |
public const uint E_DUPLICATE_READER = 0x8010001B; | |
public const uint E_CARD_UNSUPPORTED = 0x8010001C; | |
public const uint E_NO_SERVICE = 0x8010001D; | |
public const uint E_SERVICE_STOPPED = 0x8010001E; | |
public const uint E_UNEXPECTED = 0x8010001F; | |
public const uint E_ICC_INSTALLATION = 0x80100020; | |
public const uint E_ICC_CREATEORDER = 0x80100021; | |
public const uint E_UNSUPPORTED_FEATURE = 0x80100022; | |
public const uint E_DIR_NOT_FOUND = 0x80100023; | |
public const uint E_FILE_NOT_FOUND = 0x80100024; | |
public const uint E_NO_DIR = 0x80100025; | |
public const uint E_NO_FILE = 0x80100026; | |
public const uint E_NO_ACCESS = 0x80100027; | |
public const uint E_WRITE_TOO_MANY = 0x80100028; | |
public const uint E_BAD_SEEK = 0x80100029; | |
public const uint E_INVALID_CHV = 0x8010002A; | |
public const uint E_UNKNOWN_RES_MNG = 0x8010002B; | |
public const uint E_NO_SUCH_CERTIFICATE = 0x8010002C; | |
public const uint E_CERTIFICATE_UNAVAILABLE = 0x8010002D; | |
public const uint E_NO_READERS_AVAILABLE = 0x8010002E; | |
public const uint E_COMM_DATA_LOST = 0x8010002F; | |
public const uint E_NO_KEY_CONTAINER = 0x80100030; | |
public const uint W_UNSUPPORTED_CARD = 0x80100065; | |
public const uint W_UNRESPONSIVE_CARD = 0x80100066; | |
public const uint W_UNPOWERED_CARD = 0x80100067; | |
public const uint W_RESET_CARD = 0x80100068; | |
public const uint W_REMOVED_CARD = 0x80100069; | |
public const uint W_SECURITY_VIOLATION = 0x8010006A; | |
public const uint W_WRONG_CHV = 0x8010006B; | |
public const uint W_CHV_BLOCKED = 0x8010006C; | |
public const uint W_EOF = 0x8010006D; | |
public const uint W_CANCELLED_BY_USER = 0x8010006E; | |
public const uint W_CARD_NOT_AUTHENTICATED = 0x8010006F; | |
#endregion | |
#region Definition of the 'native' structures | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
public struct READERSTATE | |
{ | |
internal string szReader; | |
internal IntPtr pvUserData; | |
internal uint dwCurrentState; | |
internal uint dwEventState; | |
internal uint cbAtr; | |
[MarshalAs | |
(UnmanagedType.ByValArray, SizeConst = 0x24, ArraySubType = | |
UnmanagedType.U1)] | |
internal byte[] rgbAtr; | |
} | |
#endregion | |
#region Static methods, provided by the 'native' WINSCARD library | |
/**f* SCARD/EstablishContext | |
* | |
* NAME | |
* SCARD.EstablishContext | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardEstablishContext | |
* | |
**/ | |
[DllImport("WinScard.dll", EntryPoint = "SCardEstablishContext")] | |
public static extern uint EstablishContext(uint dwScope, | |
IntPtr nNotUsed1, | |
IntPtr nNotUsed2, | |
ref IntPtr phContext); | |
/**f* SCARD/ReleaseContext | |
* | |
* NAME | |
* SCARD.ReleaseContext | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardReleaseContext | |
* | |
**/ | |
[DllImport("WinScard.dll", EntryPoint = "SCardReleaseContext")] | |
public static extern uint ReleaseContext(IntPtr Context); | |
/**f* SCARD/ListReaders | |
* | |
* NAME | |
* SCARD.ListReaders | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardListReaders (UNICODE implementation) | |
* | |
**/ | |
[DllImport | |
("winscard.dll", EntryPoint = "SCardListReadersW", SetLastError = | |
true, CharSet = | |
CharSet.Unicode)] | |
public static extern uint ListReaders(IntPtr context, | |
string groups, | |
string readers, | |
ref uint size); | |
/**f* SCARD/GetStatusChange | |
* | |
* NAME | |
* SCARD.GetStatusChange | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardGetStatusChange (UNICODE implementation) | |
* | |
**/ | |
[DllImport | |
("winscard.dll", EntryPoint = "SCardGetStatusChangeW", CharSet = | |
CharSet. | |
Unicode)] | |
public static extern uint GetStatusChange(IntPtr hContext, | |
uint dwTimeout, [In, | |
Out, | |
MarshalAs | |
(UnmanagedType. | |
LPArray, | |
SizeParamIndex | |
= | |
3)] | |
SCARD. | |
READERSTATE | |
[] rgReaderState, | |
uint cReaders); | |
/**f* SCARD/Connect | |
* | |
* NAME | |
* SCARD.Connect | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardConnect (UNICODE implementation) | |
* | |
**/ | |
[DllImport | |
("WinScard.dll", EntryPoint = "SCardConnectW", CharSet = | |
CharSet.Unicode)] | |
public static extern uint Connect(IntPtr hContext, | |
string cReaderName, | |
uint dwShareMode, | |
uint dwPrefProtocol, | |
ref IntPtr phCard, | |
ref uint | |
ActiveProtocol); | |
/**f* SCARD/Reconnect | |
* | |
* NAME | |
* SCARD.Reconnect | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardReconnect | |
* | |
**/ | |
[DllImport("winscard.dll", EntryPoint = "SCardReconnect")] | |
public static extern uint Reconnect(IntPtr hCard, | |
uint dwShareMode, | |
uint dwPrefProtocol, uint swInit, | |
ref uint ActiveProtocol); | |
/**f* SCARD/Disconnect | |
* | |
* NAME | |
* SCARD.Disconnect | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardDisconnect | |
* | |
**/ | |
[DllImport("WinScard.dll", EntryPoint = "SCardDisconnect")] | |
public static extern uint Disconnect(IntPtr hCard, uint Disposition); | |
/**f* SCARD/Status | |
* | |
* NAME | |
* SCARD.Status | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardStatus (UNICODE version) | |
* | |
**/ | |
[DllImport | |
("winscard.dll", EntryPoint = "SCardStatusW", SetLastError = | |
true, CharSet = | |
CharSet.Unicode)] | |
public static extern uint Status(IntPtr hCard, | |
IntPtr | |
mszReaderNames, | |
ref uint | |
pcchReaderLen, | |
ref uint readerState, | |
ref uint protocol, | |
[In, | |
Out] | |
byte[] atr_bytes, | |
ref uint atr_length); | |
/**f* SCARD/Transmit | |
* | |
* NAME | |
* SCARD.Transmit | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardTransmit | |
* | |
**/ | |
[DllImport | |
("winscard.dll", EntryPoint = "SCardTransmit", SetLastError = | |
true)] | |
public static extern uint Transmit(IntPtr hCard, | |
IntPtr pioSendPci, | |
byte[] pbSendBuffer, | |
uint cbSendLength, | |
IntPtr pioRecvPci, [In, | |
Out] | |
byte[] pbRecvBuffer, [In, | |
Out] ref | |
uint pcbRecvLength); | |
/**f* SCARD/Control | |
* | |
* NAME | |
* SCARD.Control | |
* | |
* DESCRIPTION | |
* .NET wrapper for SCardControl | |
* | |
**/ | |
[DllImport | |
("winscard.dll", EntryPoint = "SCardControl", SetLastError = | |
true)] | |
public static extern uint Control(IntPtr hCard, uint ctlCode, | |
[In] byte[] pbSendBuffer, | |
uint cbSendLength, [In, | |
Out] | |
byte[] pbRecvBuffer, | |
uint RecvBuffsize, | |
ref uint pcbRecvLength); | |
#endregion | |
#region Static methods - easy access to the list of readers, and the status of each reader | |
private static string[] getReaderList() | |
{ | |
int i, j = 0; | |
string s = ""; | |
uint rc; | |
IntPtr hContext = IntPtr.Zero; | |
uint readers_size = 0; | |
int readers_count = 0; | |
string[] readers = new string[0]; | |
rc = | |
SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, | |
IntPtr.Zero, ref hContext); | |
if (rc != SCARD.S_SUCCESS) | |
return readers; | |
rc = SCARD.ListReaders(hContext, null, null, ref readers_size); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
SCARD.ReleaseContext(hContext); | |
return readers; | |
} | |
string readers_str = new string(' ', (int)readers_size); | |
rc = SCARD.ListReaders(hContext, null, readers_str, ref readers_size); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
SCARD.ReleaseContext(hContext); | |
return readers; | |
} | |
SCARD.ReleaseContext(hContext); | |
for (i = 0; i < readers_size; i++) | |
{ | |
if (readers_str[i] == '\0') | |
{ | |
if (i > 0) | |
readers_count++; | |
if (readers_str[i + 1] == '\0') | |
break; | |
} | |
} | |
if (readers_count == 0) | |
return readers; | |
readers = new string[readers_count]; | |
for (i = 0; i < readers_size; i++) | |
{ | |
if (readers_str[i] == '\0') | |
{ | |
readers[j++] = s; | |
if (readers_str[i + 1] == '\0') | |
break; | |
s = ""; | |
} | |
else | |
{ | |
s = s + readers_str[i]; | |
} | |
} | |
return readers; | |
} | |
/**f* SCARD/Readers | |
* | |
* NAME | |
* SCARD.Readers | |
* | |
* DESCRIPTION | |
* Provides the list of the connected PC/SC readers | |
* | |
* SYNOPSIS | |
* string[] SCARD.Readers | |
* | |
**/ | |
public static string[] Readers | |
{ | |
get | |
{ | |
return getReaderList(); | |
} | |
} | |
#endregion | |
#region Static methods - helpers to format status and errors | |
/**f* SCARD/ErrorToString | |
* | |
* NAME | |
* SCARD.ErrorToString | |
* | |
* DESCRIPTION | |
* Translate a PC/SC error code into a user-readable string | |
* | |
* SYNOPSIS | |
* string SCARD.ErrorToString( uint code ); | |
* | |
**/ | |
public static string ErrorToString(uint code) | |
{ | |
switch (code) | |
{ | |
case SCARD.S_SUCCESS: | |
return "SCARD_S_SUCCESS"; | |
case SCARD.F_INTERNAL_ERROR: | |
return "SCARD_F_INTERNAL_ERROR"; | |
case SCARD.E_CANCELLED: | |
return "SCARD_E_CANCELLED"; | |
case SCARD.E_INVALID_HANDLE: | |
return "SCARD_E_INVALID_HANDLE"; | |
case SCARD.E_INVALID_PARAMETER: | |
return "SCARD_E_INVALID_PARAMETER"; | |
case SCARD.E_INVALID_TARGET: | |
return "SCARD_E_INVALID_TARGET"; | |
case SCARD.E_NO_MEMORY: | |
return "SCARD_E_NO_MEMORY"; | |
case SCARD.F_WAITED_TOO_LONG: | |
return "SCARD_F_WAITED_TOO_LONG"; | |
case SCARD.E_INSUFFICIENT_BUFFER: | |
return "SCARD_E_INSUFFICIENT_BUFFER"; | |
case SCARD.E_UNKNOWN_READER: | |
return "SCARD_E_UNKNOWN_READER"; | |
case SCARD.E_TIMEOUT: | |
return "SCARD_E_TIMEOUT"; | |
case SCARD.E_SHARING_VIOLATION: | |
return "SCARD_E_SHARING_VIOLATION"; | |
case SCARD.E_NO_SMARTCARD: | |
return "SCARD_E_NO_SMARTCARD"; | |
case SCARD.E_UNKNOWN_CARD: | |
return "SCARD_E_UNKNOWN_CARD"; | |
case SCARD.E_CANT_DISPOSE: | |
return "SCARD_E_CANT_DISPOSE"; | |
case SCARD.E_PROTO_MISMATCH: | |
return "SCARD_E_PROTO_MISMATCH"; | |
case SCARD.E_NOT_READY: | |
return "SCARD_E_NOT_READY"; | |
case SCARD.E_INVALID_VALUE: | |
return "SCARD_E_INVALID_VALUE"; | |
case SCARD.E_SYSTEM_CANCELLED: | |
return "SCARD_E_SYSTEM_CANCELLED"; | |
case SCARD.F_COMM_ERROR: | |
return "SCARD_F_COMM_ERROR"; | |
case SCARD.F_UNKNOWN_ERROR: | |
return "SCARD_F_UNKNOWN_ERROR"; | |
case SCARD.E_INVALID_ATR: | |
return "SCARD_E_INVALID_ATR"; | |
case SCARD.E_NOT_TRANSACTED: | |
return "SCARD_E_NOT_TRANSACTED"; | |
case SCARD.E_READER_UNAVAILABLE: | |
return "SCARD_E_READER_UNAVAILABLE"; | |
case SCARD.P_SHUTDOWN: | |
return "SCARD_P_SHUTDOWN"; | |
case SCARD.E_PCI_TOO_SMALL: | |
return "SCARD_E_PCI_TOO_SMALL"; | |
case SCARD.E_READER_UNSUPPORTED: | |
return "SCARD_E_READER_UNSUPPORTED"; | |
case SCARD.E_DUPLICATE_READER: | |
return "SCARD_E_DUPLICATE_READER"; | |
case SCARD.E_CARD_UNSUPPORTED: | |
return "SCARD_E_CARD_UNSUPPORTED"; | |
case SCARD.E_NO_SERVICE: | |
return "SCARD_E_NO_SERVICE"; | |
case SCARD.E_SERVICE_STOPPED: | |
return "SCARD_E_SERVICE_STOPPED"; | |
case SCARD.E_UNEXPECTED: | |
return "SCARD_E_UNEXPECTED"; | |
case SCARD.E_ICC_INSTALLATION: | |
return "SCARD_E_ICC_INSTALLATION"; | |
case SCARD.E_ICC_CREATEORDER: | |
return "SCARD_E_ICC_CREATEORDER"; | |
case SCARD.E_UNSUPPORTED_FEATURE: | |
return "SCARD_E_UNSUPPORTED_FEATURE"; | |
case SCARD.E_DIR_NOT_FOUND: | |
return "SCARD_E_DIR_NOT_FOUND"; | |
case SCARD.E_FILE_NOT_FOUND: | |
return "SCARD_E_FILE_NOT_FOUND"; | |
case SCARD.E_NO_DIR: | |
return "SCARD_E_NO_DIR"; | |
case SCARD.E_NO_FILE: | |
return "SCARD_E_NO_FILE"; | |
case SCARD.E_NO_ACCESS: | |
return "SCARD_E_NO_ACCESS"; | |
case SCARD.E_WRITE_TOO_MANY: | |
return "SCARD_E_WRITE_TOO_MANY"; | |
case SCARD.E_BAD_SEEK: | |
return "SCARD_E_BAD_SEEK"; | |
case SCARD.E_INVALID_CHV: | |
return "SCARD_E_INVALID_CHV"; | |
case SCARD.E_UNKNOWN_RES_MNG: | |
return "SCARD_E_UNKNOWN_RES_MNG"; | |
case SCARD.E_NO_SUCH_CERTIFICATE: | |
return "SCARD_E_NO_SUCH_CERTIFICATE"; | |
case SCARD.E_CERTIFICATE_UNAVAILABLE: | |
return "SCARD_E_CERTIFICATE_UNAVAILABLE"; | |
case SCARD.E_NO_READERS_AVAILABLE: | |
return "SCARD_E_NO_READERS_AVAILABLE"; | |
case SCARD.E_COMM_DATA_LOST: | |
return "SCARD_E_COMM_DATA_LOST"; | |
case SCARD.E_NO_KEY_CONTAINER: | |
return "SCARD_E_NO_KEY_CONTAINER"; | |
//case SCARD.E_SERVER_TOO_BUSY : return "SCARD_E_SERVER_TOO_BUSY"; | |
case SCARD.W_UNSUPPORTED_CARD: | |
return "SCARD_W_UNSUPPORTED_CARD"; | |
case SCARD.W_UNRESPONSIVE_CARD: | |
return "SCARD_W_UNRESPONSIVE_CARD"; | |
case SCARD.W_UNPOWERED_CARD: | |
return "SCARD_W_UNPOWERED_CARD"; | |
case SCARD.W_RESET_CARD: | |
return "SCARD_W_RESET_CARD"; | |
case SCARD.W_REMOVED_CARD: | |
return "SCARD_W_REMOVED_CARD"; | |
case SCARD.W_SECURITY_VIOLATION: | |
return "SCARD_W_SECURITY_VIOLATION"; | |
case SCARD.W_WRONG_CHV: | |
return "SCARD_W_WRONG_CHV"; | |
case SCARD.W_CHV_BLOCKED: | |
return "SCARD_W_CHV_BLOCKED"; | |
case SCARD.W_EOF: | |
return "SCARD_W_EOF"; | |
case SCARD.W_CANCELLED_BY_USER: | |
return "SCARD_W_CANCELLED_BY_USER"; | |
case SCARD.W_CARD_NOT_AUTHENTICATED: | |
return "SCARD_W_CARD_NOT_AUTHENTICATED"; | |
default: | |
return ""; | |
} | |
} | |
/**f* SCARD/ReaderStatusToString | |
* | |
* NAME | |
* SCARD.ReaderStatusToString | |
* | |
* DESCRIPTION | |
* Translate the Status of the reader into a user-readable string | |
* | |
* SYNOPSIS | |
* string SCARD.ReaderStatusToString( uint state ); | |
* | |
**/ | |
public static string ReaderStatusToString(uint state) | |
{ | |
string r = ""; | |
if (state == SCARD.STATE_UNAWARE) | |
r += ",UNAWARE"; | |
if ((state & SCARD.STATE_EMPTY) != 0) | |
r += ",EMPTY"; | |
if ((state & SCARD.STATE_PRESENT) != 0) | |
r += ",PRESENT"; | |
if ((state & SCARD.STATE_MUTE) != 0) | |
r += ",MUTE"; | |
if ((state & SCARD.STATE_UNPOWERED) != 0) | |
r += ",UNPOWERED"; | |
if ((state & SCARD.STATE_ATRMATCH) != 0) | |
r += ",ATRMATCH"; | |
if ((state & SCARD.STATE_EXCLUSIVE) != 0) | |
r += ",EXCLUSIVE"; | |
if ((state & SCARD.STATE_INUSE) != 0) | |
r += ",INUSE"; | |
if ((state & SCARD.STATE_IGNORE) != 0) | |
r += ",IGNORE"; | |
if ((state & SCARD.STATE_UNKNOWN) != 0) | |
r += ",UNKNOWN"; | |
if ((state & SCARD.STATE_UNAVAILABLE) != 0) | |
r += ",UNAVAILABLE"; | |
if ((state & SCARD.STATE_CHANGED) != 0) | |
r += ",CHANGED"; | |
if (r.Length >= 1) | |
r = r.Substring(1); | |
return r; | |
} | |
/**f* SCARD/CardProtocolToString | |
* | |
* NAME | |
* SCARD.CardProtocolToString | |
* | |
* DESCRIPTION | |
* Translate the Protocol of the card into a user-readable string | |
* | |
* SYNOPSIS | |
* string SCARD.CardProtocolToString( uint protocol ); | |
* | |
**/ | |
public static string CardProtocolToString(uint protocol) | |
{ | |
if (protocol == SCARD.PROTOCOL_T0) | |
return "T=0"; | |
if (protocol == SCARD.PROTOCOL_T1) | |
return "T=1"; | |
if (protocol == SCARD.PROTOCOL_RAW) | |
return "RAW"; | |
if (protocol == 0) | |
return "DIRECT"; | |
return "Unknown"; | |
} | |
/**f* SCARD/CardShareModeToString | |
* | |
* NAME | |
* SCARD.CardShareModeToString | |
* | |
* DESCRIPTION | |
* Translate the Share Mode of the card into a user-readable string | |
* | |
* SYNOPSIS | |
* string SCARD.CardShareModeToString( uint share_mode ); | |
* | |
**/ | |
public static string CardShareModeToString(uint share_mode) | |
{ | |
if (share_mode == SCARD.SHARE_SHARED) | |
return "SHARED"; | |
if (share_mode == SCARD.SHARE_EXCLUSIVE) | |
return "EXCLUSIVE"; | |
if (share_mode == SCARD.SHARE_DIRECT) | |
return "DIRECT"; | |
return "Unknown"; | |
} | |
/**f* SCARD/CardStatusWordsToString | |
* | |
* NAME | |
* SCARD.CardStatusWordsToString | |
* | |
* DESCRIPTION | |
* Translate the Status Word of the card into a user-readable string | |
* | |
* SYNOPSIS | |
* string SCARD.CardStatusWordsToString( byte SW1, byte SW2 ); | |
* string SCARD.CardStatusWordsToString( ushort SW ); | |
* | |
**/ | |
public static string CardStatusWordsToString(ushort SW) | |
{ | |
byte SW1 = (byte)(SW / 0x0100); | |
byte SW2 = (byte)(SW % 0x0100); | |
return CardStatusWordsToString(SW1, SW2); | |
} | |
public static string CardStatusWordsToString(byte SW1, byte SW2) | |
{ | |
switch (SW1) | |
{ | |
case 0x60: | |
return "null"; | |
case 0x61: | |
return "Still " + SW2.ToString() + | |
" bytes available. Use GET RESPONSE to access this data"; | |
case 0x62: | |
switch (SW2) | |
{ | |
case 0x81: | |
return "Warning : returned data may be corrupted"; | |
case 0x82: | |
return "Warning : EoF has been reached before ending"; | |
case 0x83: | |
return "Warning : selected file invalidated"; | |
case 0x84: | |
return "Warning : bad file control information format"; | |
default: | |
return "Warning : state unchanged"; | |
} | |
case 0x63: | |
if ((SW2 >= 0xC0) && (SW2 <= 0xCF)) | |
return "Warning : counter value is " + SW2.ToString(); | |
if (SW2 == 81) | |
return "Warning : file filled up with last write"; | |
return "Warning : state unchanged"; | |
case 0x64: | |
return "Error : state unchanged"; | |
case 0x65: | |
switch (SW2) | |
{ | |
case 0x01: | |
return "Memory failure, problem in writing the EEPROM"; | |
case 0x81: | |
return "Error : memory failure"; | |
default: | |
return "Error : state changed"; | |
} | |
case 0x66: | |
return "Security error"; | |
case 0x67: | |
return "Check error - incorrect byte length"; | |
case 0x68: | |
switch (SW2) | |
{ | |
case 0x81: | |
return "Check error - logical channel not supported"; | |
case 0x82: | |
return "Check error : secure messaging not supported"; | |
default: | |
return "Check error : request function not supported"; | |
} | |
case 0x69: | |
switch (SW2) | |
{ | |
case 0x81: | |
return "Check error : command incompatible with file structure"; | |
case 0x82: | |
return "Check error : security status not statisfied"; | |
case 0x83: | |
return "Check error : authentication method blocked"; | |
case 0x84: | |
return "Check error : referenced data invalidated"; | |
case 0x85: | |
return "Check error : conditions of use not satisfied"; | |
case 0x86: | |
return "Check error : command not allowed (no current EF)"; | |
case 0x87: | |
return "Check error : Expected SM data objects missing"; | |
case 0x88: | |
return "Check error : SM data objects incorrect "; | |
default: | |
return | |
"Unknow command, most probably erroneous typing, protocol violation or incorrect format"; | |
} | |
case 0x6A: | |
switch (SW2) | |
{ | |
case 0x00: | |
return "Check error : P1 or P2 incorrect"; | |
case 0x80: | |
return "Check error : parameters in data field incorrect"; | |
case 0x81: | |
return "Check error : function not supported"; | |
case 0x82: | |
return "Check error : file not found"; | |
case 0x83: | |
return "Check error : record not found"; | |
case 0x84: | |
return "Check error : insufficient memory space in this file"; | |
case 0x85: | |
return "Check error : Lc inconsistant with TLV structure"; | |
case 0x86: | |
return "Check error : inconsistant parameters P1-P2"; | |
case 0x87: | |
return "Check error : P3 inconsistant with P1-P2"; | |
case 0x88: | |
return "Check error : referenced data not found"; | |
default: | |
return "Check error : wrong parameters"; | |
} | |
case 0x6B: | |
return "Check error : reference P1,P2 incorrect"; | |
case 0x6C: | |
return "Lc length incorrect, correct length :" + SW2.ToString(); | |
case 0x6D: | |
return "Ins invalid or unsupported"; | |
case 0x6E: | |
return "Cla insupported"; | |
case 0x6F: | |
return "Undiagnosticed error"; | |
case 0x90: | |
switch (SW2) | |
{ | |
case 0x00: | |
return "Success"; | |
case 0x01: | |
return "Failed to write EEPROM"; | |
case 0x10: | |
return "Wrong PIN (1st try)"; | |
case 0x20: | |
return "Wrong PIN(2nd try)"; | |
case 0x40: | |
return "Wrong PIN (3rd try)"; | |
case 0x80: | |
return "Wrong PIN, card blocked"; | |
default: | |
return "Unknown error"; | |
} | |
case 0x92: | |
switch (SW2) | |
{ | |
case 0x00: | |
return "Reference executed ok"; | |
case 0x02: | |
return "Failed to write EEPROM"; | |
default: | |
return "Unknow error"; | |
} | |
case 0x94: | |
return "No EF selected"; | |
case 0x98: | |
switch (SW2) | |
{ | |
case 0x02: | |
return "Invalid PIN"; | |
case 0x04: | |
return "Wrong PIN presentation"; | |
case 0x06: | |
return "PIN cancelled"; | |
case 0x08: | |
return "PIN inactivated"; | |
case 0x10: | |
return "Security condition unsatisfied"; | |
case 0x20: | |
return "PIN inactive"; | |
default: | |
return "Unknown error"; | |
} | |
default: | |
return "Unknown error"; | |
} | |
} | |
#endregion | |
} | |
#region SCardReader class | |
/**c* SpringCardPCSC/SCardReader | |
* | |
* NAME | |
* SCardReader | |
* | |
* DESCRIPTION | |
* The SCardReader object is used to monitor a PC/SC reader (i.e. wait for card arrival) | |
* | |
* SYNOPSIS | |
* SCardReader( string reader_name ); | |
* | |
**/ | |
public class SCardReader | |
{ | |
uint _last_error; | |
string _reader_name; | |
uint _reader_state = SCARD.STATE_UNAWARE; | |
CardBuffer _card_atr = null; | |
StatusChangeCallback _status_change_callback = null; | |
Thread _status_change_thread = null; | |
volatile bool _status_change_running = false; | |
public SCardReader(string reader_name) | |
{ | |
_reader_name = reader_name; | |
} | |
~SCardReader() | |
{ | |
StopMonitor(); | |
} | |
/**v* SCardReader/Name | |
* | |
* NAME | |
* SCardReader.Name | |
* | |
* SYNOPSIS | |
* string Name | |
* | |
* OUTPUT | |
* Return the name of the reader specified when instanciating the object. | |
* | |
**/ | |
public string Name | |
{ | |
get | |
{ | |
return _reader_name; | |
} | |
} | |
/**t* SCardReader/StatusChangeCallback | |
* | |
* NAME | |
* SCardReader.StatusChangeCallback | |
* | |
* SYNOPSIS | |
* delegate void StatusChangeCallback(uint ReaderState, CardBuffer CardAtr); | |
* | |
* DESCRIPTION | |
* Typedef for the callback that will be called by the background thread launched | |
* by SCardReader.StartMonitor(), everytime the status of the reader is changed. | |
* | |
* NOTES | |
* The callback is invoked in the context of a background thread. This implies that | |
* it is not allowed to access the GUI's components directly. | |
* | |
**/ | |
public delegate void StatusChangeCallback(uint ReaderState, | |
CardBuffer CardAtr); | |
/**m* SCardReader/StartMonitor | |
* | |
* NAME | |
* SCardReader.StartMonitor() | |
* | |
* SYNOPSIS | |
* SCardReader.StartMonitor(SCardReader.StatusChangeCallback callback); | |
* | |
* DESCRIPTION | |
* Create a background thread to monitor the reader associated to the object. | |
* Everytime the status of the reader is changed, the callback is invoked. | |
* | |
* SEE ALSO | |
* SCardReader.StatusChangeCallback | |
* SCardReader.StopMonitor() | |
* | |
**/ | |
public void StartMonitor(StatusChangeCallback callback) | |
{ | |
StopMonitor(); | |
if (callback != null) | |
{ | |
_status_change_callback = callback; | |
_status_change_thread = new Thread(StatusChangeMonitor); | |
_status_change_running = true; | |
_status_change_thread.Start(); | |
} | |
} | |
/**m* SCardReader/StopMonitor | |
* | |
* NAME | |
* SCardReader.StopMonitor() | |
* | |
* DESCRIPTION | |
* Stop the background thread previously launched by SCardReader.StartMonitor(). | |
* | |
**/ | |
public void StopMonitor() | |
{ | |
_status_change_callback = null; | |
_status_change_running = false; | |
if (_status_change_thread != null) | |
{ | |
_status_change_thread.Abort(); | |
_status_change_thread.Join(); | |
_status_change_thread = null; | |
} | |
} | |
private void StatusChangeMonitor() | |
{ | |
uint rc; | |
IntPtr hContext = IntPtr.Zero; | |
_reader_state = SCARD.STATE_UNAWARE; | |
_card_atr = null; | |
rc = | |
SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, | |
IntPtr.Zero, ref hContext); | |
if (rc != SCARD.S_SUCCESS) | |
return; | |
SCARD.READERSTATE[] states = new SCARD.READERSTATE[1]; | |
states[0] = new SCARD.READERSTATE(); | |
states[0].szReader = _reader_name; | |
states[0].pvUserData = IntPtr.Zero; | |
states[0].dwCurrentState = 0; | |
states[0].dwEventState = 0; | |
states[0].cbAtr = 0; | |
states[0].rgbAtr = null; | |
while (_status_change_running) | |
{ | |
rc = SCARD.GetStatusChange(hContext, 250, states, 1); | |
if (!_status_change_running) | |
break; | |
if (rc == SCARD.E_TIMEOUT) | |
continue; | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_last_error = rc; | |
SCARD.ReleaseContext(hContext); | |
if (_status_change_callback != null) | |
_status_change_callback(0, null); | |
break; | |
} | |
states[0].dwCurrentState = states[0].dwEventState; | |
if ((states[0].dwEventState & SCARD.STATE_CHANGED) != 0) | |
{ | |
if (_status_change_callback != null) | |
{ | |
CardBuffer card_atr = null; | |
if ((states[0].dwEventState & SCARD.STATE_PRESENT) != 0) | |
card_atr = | |
new CardBuffer(states[0].rgbAtr, (int)states[0].cbAtr); | |
_status_change_callback(states[0].dwEventState & ~SCARD. | |
STATE_CHANGED, card_atr); | |
} | |
} | |
} | |
SCARD.ReleaseContext(hContext); | |
} | |
private void UpdateState() | |
{ | |
uint rc; | |
IntPtr hContext = IntPtr.Zero; | |
_reader_state = SCARD.STATE_UNAWARE; | |
_card_atr = null; | |
rc = | |
SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, | |
IntPtr.Zero, ref hContext); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_last_error = rc; | |
return; | |
} | |
SCARD.READERSTATE[] states = new SCARD.READERSTATE[1]; | |
states[0] = new SCARD.READERSTATE(); | |
states[0].szReader = _reader_name; | |
states[0].pvUserData = IntPtr.Zero; | |
states[0].dwCurrentState = 0; | |
states[0].dwEventState = 0; | |
states[0].cbAtr = 0; | |
states[0].rgbAtr = null; | |
rc = SCARD.GetStatusChange(hContext, 0, states, 1); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
SCARD.ReleaseContext(hContext); | |
return; | |
} | |
SCARD.ReleaseContext(hContext); | |
_reader_state = states[0].dwEventState; | |
if ((_reader_state & SCARD.STATE_PRESENT) != 0) | |
{ | |
_card_atr = new CardBuffer(states[0].rgbAtr, (int)states[0].cbAtr); | |
} | |
} | |
/**v* SCardReader/Status | |
* | |
* NAME | |
* SCardReader.Status | |
* | |
* SYNOPSIS | |
* uint Status | |
* | |
* OUTPUT | |
* Returns the current status of the reader. | |
* | |
* SEE ALSO | |
* SCardReader.CardPresent | |
* SCardReader.StatusAsString | |
* | |
**/ | |
public uint Status | |
{ | |
get | |
{ | |
UpdateState(); | |
return _reader_state; | |
} | |
} | |
/**v* SCardReader/StatusAsString | |
* | |
* NAME | |
* SCardReader.StatusAsString | |
* | |
* SYNOPSIS | |
* string StatusAsString | |
* | |
* OUTPUT | |
* Returns the current status of the reader, using SCARD.ReaderStatusToString as formatter. | |
* | |
* SEE ALSO | |
* SCardReader.Status | |
* | |
**/ | |
public string StatusAsString | |
{ | |
get | |
{ | |
UpdateState(); | |
return SCARD.ReaderStatusToString(_reader_state); | |
} | |
} | |
/**v* SCardReader/CardPresent | |
* | |
* NAME | |
* SCardReader.CardPresent | |
* | |
* SYNOPSIS | |
* bool CardPresent | |
* | |
* OUTPUT | |
* Returns true if a card is present in the reader. | |
* Returns false if there's no smartcard in the reader. | |
* | |
* SEE ALSO | |
* SCardReader.CardAtr | |
* SCardReader.CardAvailable | |
* SCardReader.Status | |
* | |
**/ | |
public bool CardPresent | |
{ | |
get | |
{ | |
UpdateState(); | |
if ((_reader_state & SCARD.STATE_PRESENT) != 0) | |
return true; | |
return false; | |
} | |
} | |
/**v* SCardReader/CardAvailable | |
* | |
* NAME | |
* SCardReader.CardAvailable | |
* | |
* SYNOPSIS | |
* bool CardAvailable | |
* | |
* OUTPUT | |
* Returns true if a card is available in the reader. | |
* Returns false if there's no smartcard in the reader, or if it is already used by another process/thread. | |
* | |
* SEE ALSO | |
* SCardReader.CardAtr | |
* SCardReader.CardPresent | |
* SCardReader.Status | |
* | |
**/ | |
public bool CardAvailable | |
{ | |
get | |
{ | |
UpdateState(); | |
if (((_reader_state & SCARD.STATE_PRESENT) != 0) | |
&& ((_reader_state & SCARD.STATE_MUTE) == 0) | |
&& ((_reader_state & SCARD.STATE_INUSE) == 0)) | |
return true; | |
return false; | |
} | |
} | |
/**v* SCardReader/CardAtr | |
* | |
* NAME | |
* SCardReader.CardAtr | |
* | |
* SYNOPSIS | |
* CardBuffer CardAtr | |
* | |
* OUTPUT | |
* If a smartcard is present in the reader (SCardReader.CardPresent == true), returns the ATR of the card. | |
* Returns null overwise. | |
* | |
* SEE ALSO | |
* SCardReader.CardPresent | |
* SCardReader.Status | |
* | |
**/ | |
public CardBuffer CardAtr | |
{ | |
get | |
{ | |
UpdateState(); | |
return _card_atr; | |
} | |
} | |
/**v* SCardReader/LastError | |
* | |
* NAME | |
* uint SCardReader.LastError | |
* | |
* OUTPUT | |
* Returns the last error encountered by the object when working with SCARD functions. | |
* | |
* SEE ALSO | |
* SCardReader.LastErrorAsString | |
* | |
**/ | |
public uint LastError | |
{ | |
get | |
{ | |
return _last_error; | |
} | |
} | |
/**v* SCardReader/LastErrorAsString | |
* | |
* NAME | |
* string SCardReader.LastErrorAsString | |
* | |
* OUTPUT | |
* Returns the last error encountered by the object when working with SCARD functions. | |
* | |
* SEE ALSO | |
* SCardReader.LastError | |
* | |
**/ | |
public string LastErrorAsString | |
{ | |
get | |
{ | |
return SCARD.ErrorToString(_last_error); | |
} | |
} | |
} | |
#endregion | |
#region SCardChannel class | |
/**c* SpringCardPCSC/SCardChannel | |
* | |
* NAME | |
* SCardChannel | |
* | |
* DESCRIPTION | |
* The SCardChannel object provides the actual connection to the smartcard through the PC/SC reader | |
* | |
* SYNOPSIS | |
* SCardChannel( string reader_name ); | |
* SCardChannel( SCardReader reader ); | |
* | |
**/ | |
public class SCardChannel | |
{ | |
private string _reader_name; | |
private uint _reader_state; | |
private uint _active_protocol; | |
private uint _want_protocols = SCARD.PROTOCOL_T0 | SCARD.PROTOCOL_T1; | |
private uint _share_mode = SCARD.SHARE_SHARED; | |
private uint _last_error; | |
private CAPDU _capdu; | |
private RAPDU _rapdu; | |
private CardBuffer _cctrl; | |
private CardBuffer _rctrl; | |
private CardBuffer _card_atr; | |
private IntPtr _hContext = IntPtr.Zero; | |
private IntPtr _hCard = IntPtr.Zero; | |
TransmitDoneCallback _transmit_done_callback = null; | |
Thread _transmit_thread = null; | |
public delegate void TransmitDoneCallback(RAPDU rapdu); | |
public SCardChannel(string reader_name) | |
{ | |
uint rc; | |
rc = | |
SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, | |
IntPtr.Zero, ref _hContext); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_hContext = IntPtr.Zero; | |
_last_error = rc; | |
} | |
_reader_name = reader_name; | |
} | |
public SCardChannel(SCardReader reader) | |
{ | |
uint rc; | |
rc = | |
SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, | |
IntPtr.Zero, ref _hContext); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_hContext = IntPtr.Zero; | |
_last_error = rc; | |
} | |
_reader_name = reader.Name; | |
} | |
~SCardChannel() | |
{ | |
if (Connected) | |
Disconnect(); | |
if (_hContext != IntPtr.Zero) | |
SCARD.ReleaseContext(_hContext); | |
} | |
public IntPtr hContext | |
{ | |
get | |
{ | |
return _hContext; | |
} | |
} | |
public IntPtr hCard | |
{ | |
get | |
{ | |
return _hCard; | |
} | |
} | |
private void UpdateState() | |
{ | |
uint rc; | |
_reader_state = SCARD.STATE_UNAWARE; | |
_card_atr = null; | |
if (Connected) | |
{ | |
byte[] atr_buffer = new byte[36]; | |
uint atr_length = 36; | |
uint dummy = 0; | |
rc = | |
SCARD.Status(_hCard, IntPtr.Zero, ref dummy, | |
ref _reader_state, ref _active_protocol, atr_buffer, | |
ref atr_length); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_last_error = rc; | |
return; | |
} | |
_card_atr = new CardBuffer(atr_buffer, (int)atr_length); | |
} | |
else | |
{ | |
SCARD.READERSTATE[] states = new SCARD.READERSTATE[1]; | |
states[0] = new SCARD.READERSTATE(); | |
states[0].szReader = _reader_name; | |
states[0].pvUserData = IntPtr.Zero; | |
states[0].dwCurrentState = 0; | |
states[0].dwEventState = 0; | |
states[0].cbAtr = 0; | |
states[0].rgbAtr = null; | |
rc = SCARD.GetStatusChange(_hContext, 0, states, 1); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_last_error = rc; | |
return; | |
} | |
_reader_state = states[0].dwEventState; | |
if ((_reader_state & SCARD.STATE_PRESENT) != 0) | |
{ | |
_card_atr = new CardBuffer(states[0].rgbAtr, (int)states[0].cbAtr); | |
} | |
} | |
} | |
/**v* SCardChannel/CardPresent | |
* | |
* NAME | |
* SCardChannel.CardPresent | |
* | |
* SYNOPSIS | |
* bool CardPresent | |
* | |
* OUTPUT | |
* Returns true if a card is present in the reader associated to the SCardChannel object. | |
* Returns false if there's no smartcard in the reader. | |
* | |
* SEE ALSO | |
* SCardChannel.CardAvailable | |
* SCardChannel.CardAtr | |
* | |
**/ | |
public bool CardPresent | |
{ | |
get | |
{ | |
UpdateState(); | |
if ((_reader_state & SCARD.STATE_PRESENT) != 0) | |
return true; | |
return false; | |
} | |
} | |
/**v* SCardChannel/CardAvailable | |
* | |
* NAME | |
* SCardChannel.CardAvailable | |
* | |
* SYNOPSIS | |
* bool CardAvailable | |
* | |
* OUTPUT | |
* Returns true if a card is available in the reader associated to the SCardChannel object. | |
* Returns false if there's no smartcard in the reader, or if it is already used by another process/thread. | |
* | |
* SEE ALSO | |
* SCardChannel.CardPresent | |
* SCardChannel.CardAtr | |
* | |
**/ | |
public bool CardAvailable | |
{ | |
get | |
{ | |
UpdateState(); | |
if (((_reader_state & SCARD.STATE_PRESENT) != 0) | |
&& ((_reader_state & SCARD.STATE_MUTE) == 0) | |
&& ((_reader_state & SCARD.STATE_INUSE) == 0)) | |
return true; | |
return false; | |
} | |
} | |
/**v* SCardChannel/CardAtr | |
* | |
* NAME | |
* SCardChannel.CardAtr | |
* | |
* SYNOPSIS | |
* CardBuffer CardAtr | |
* | |
* OUTPUT | |
* Returns the ATR of the smartcard in the reader, or null if no smartcard is present. | |
* | |
* SEE ALSO | |
* SCardChannel.CardPresent | |
* | |
**/ | |
public CardBuffer CardAtr | |
{ | |
get | |
{ | |
UpdateState(); | |
return _card_atr; | |
} | |
} | |
/**v* SCardChannel/Connected | |
* | |
* NAME | |
* SCardChannel.Connected | |
* | |
* SYNOPSIS | |
* bool Connected | |
* | |
* OUTPUT | |
* Returns true if the SCardChannel object is actually connected to a smartcard. | |
* Returns false if not. | |
* | |
**/ | |
public bool Connected | |
{ | |
get | |
{ | |
if (_hCard != IntPtr.Zero) | |
return true; | |
return false; | |
} | |
} | |
/**v* SCardChannel/Protocol | |
* | |
* NAME | |
* SCardChannel.Protocol | |
* | |
* SYNOPSIS | |
* uint Protocol | |
* | |
* INPUTS | |
* Before the smartcard has been | |
* | |
* | |
* , set Protocol to specify the communication protocol(s) to be used | |
* by Connect(). Allowed values are SCARD.PROTOCOL_T0, SCARD.PROTOCOL_T1 or SCARD.PROTOCOL_T0|SCARD.PROTOCOL_T1. | |
* | |
* OUTPUT | |
* Once the smartcard has been connected (Connected == true), Protocol is the current communication protocol. | |
* Possible values are SCARD.PROTOCOL_T0 or SCARD.PROTOCOL_T1. | |
* | |
* SEE ALSO | |
* SCardChannel.ProtocolAsString | |
* | |
**/ | |
public uint Protocol | |
{ | |
get | |
{ | |
return _active_protocol; | |
} | |
set | |
{ | |
_want_protocols = value; | |
} | |
} | |
/**v* SCardChannel/ProtocolAsString | |
* | |
* NAME | |
* SCardChannel.ProtocolAsString | |
* | |
* SYNOPSIS | |
* string ProtocolAsString | |
* | |
* INPUTS | |
* Before the smartcard has been connected, set ProtocolAsString to specify the communication protocol(s) to be used | |
* by Connect(). Allowed values are "T=0", "T=1" or "*" (or "T=0|T=1"). | |
* | |
* OUTPUT | |
* Once the smartcard has been connected (Connected == true), ProtocolAsString is the current communication protocol. | |
* Possible values are "T=0" or "T=1". | |
* | |
* SEE ALSO | |
* SCardChannel.Protocol | |
* | |
**/ | |
public string ProtocolAsString | |
{ | |
get | |
{ | |
return SCARD.CardProtocolToString(_active_protocol); | |
} | |
set | |
{ | |
value = value.ToUpper(); | |
if (value == "T=0") | |
{ | |
_want_protocols = SCARD.PROTOCOL_T0; | |
} | |
else if (value == "T=1") | |
{ | |
_want_protocols = SCARD.PROTOCOL_T1; | |
} | |
else | |
if ((value == "*") || (value == "AUTO") || (value == "T=0|T=1")) | |
{ | |
_want_protocols = SCARD.PROTOCOL_T0 | SCARD.PROTOCOL_T1; | |
} | |
else if ((value == "") || (value == "DIRECT")) | |
{ | |
_want_protocols = 0; | |
_share_mode = SCARD.SHARE_DIRECT; | |
} | |
} | |
} | |
/**v* SCardChannel/ShareMode | |
* | |
* NAME | |
* SCardChannel.ShareMode | |
* | |
* SYNOPSIS | |
* uint ShareMode | |
* | |
* INPUTS | |
* Before the smartcard has been connected, set ShareMode to specify the sharing mode to be used | |
* by Connect(). Allowed values are SCARD.SHARE_EXCLUSIVE, SCARD.SHARE_SHARED or SCARD.SHARE_DIRECT. | |
* | |
* SEE ALSO | |
* SCardChannel.ShareModeAsString | |
* | |
**/ | |
public uint ShareMode | |
{ | |
get | |
{ | |
return _share_mode; | |
} | |
set | |
{ | |
_share_mode = value; | |
} | |
} | |
/**v* SCardChannel/ShareModeAsString | |
* | |
* NAME | |
* SCardChannel.ShareModeAsString | |
* | |
* SYNOPSIS | |
* string ShareModeAsString | |
* | |
* INPUTS | |
* Before the smartcard has been connected, set ShareModeAsString to specify the sharing mode to be used | |
* by Connect(). Allowed values are "EXCLUSIVE", "SHARED" or "DIRECT". | |
* | |
* SEE ALSO | |
* SCardChannel.ShareMode | |
* | |
**/ | |
public string ShareModeAsString | |
{ | |
get | |
{ | |
return SCARD.CardShareModeToString(_share_mode); | |
} | |
set | |
{ | |
value = value.ToUpper(); | |
if (value == "EXCLUSIVE") | |
{ | |
_share_mode = SCARD.SHARE_EXCLUSIVE; | |
} | |
else if (value == "SHARED") | |
{ | |
_share_mode = SCARD.SHARE_SHARED; | |
} | |
else if (value == "DIRECT") | |
{ | |
_want_protocols = 0; | |
_share_mode = SCARD.SHARE_DIRECT; | |
} | |
} | |
} | |
/**m* SCardChannel/Connect | |
* | |
* NAME | |
* SCardChannel.Connect() | |
* | |
* SYNOPSIS | |
* bool Connect() | |
* | |
* DESCRIPTION | |
* Open the connection channel to the smartcard (according to the specified Protocol, default is either T=0 or T=1) | |
* | |
* OUTPUT | |
* Returns true if the connection has been successfully established. | |
* Returns false if not. See LastError for details. | |
* | |
* SEE ALSO | |
* SCardChannel.CardPresent | |
* SCardChannel.CardAtr | |
* SCardChannel.Protocol | |
* SCardChannel.Transmit | |
* | |
**/ | |
public bool Connect() | |
{ | |
uint rc; | |
if (Connected) | |
return false; | |
rc = | |
SCARD.Connect(_hContext, _reader_name, _share_mode, _want_protocols, | |
ref _hCard, ref _active_protocol); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_hCard = IntPtr.Zero; | |
_last_error = rc; | |
return false; | |
} | |
UpdateState(); | |
return true; | |
} | |
/**m* SCardChannel/Disconnect | |
* | |
* NAME | |
* SCardChannel.Disconnect() | |
* | |
* SYNOPSIS | |
* void Disconnect() | |
* void Disconnect(uint disposition) | |
* | |
* DESCRIPTION | |
* Close the connection channel | |
* | |
* INPUTS | |
* The disposition parameter must take one of the following values: | |
* - SCARD.EJECT_CARD | |
* - SCARD.UNPOWER_CARD | |
* - SCARD.RESET_CARD | |
* - SCARD.LEAVE_CARD | |
* If this parameter is omitted, it defaults to SCARD.RESET_CARD | |
* | |
* SEE ALSO | |
* SCardChannel.Connect | |
* | |
**/ | |
public void Disconnect(uint disposition) | |
{ | |
uint rc; | |
rc = SCARD.Disconnect(_hCard, disposition); | |
if (rc != SCARD.S_SUCCESS) | |
_last_error = rc; | |
_hCard = IntPtr.Zero; | |
} | |
public void Disconnect() | |
{ | |
DisconnectReset(); | |
} | |
/**m* SCardChannel/DisconnectEject | |
* | |
* NAME | |
* SCardChannel.DisconnectEject() | |
* | |
* SYNOPSIS | |
* void DisconnectEject() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Disconnect(SCARD.EJECT_CARD) | |
* | |
**/ | |
public void DisconnectEject() | |
{ | |
Disconnect(SCARD.EJECT_CARD); | |
} | |
/**m* SCardChannel/DisconnectUnpower | |
* | |
* NAME | |
* SCardChannel.DisconnectUnpower() | |
* | |
* SYNOPSIS | |
* void DisconnectUnpower() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Disconnect(SCARD.UNPOWER_CARD) | |
* | |
**/ | |
public void DisconnectUnpower() | |
{ | |
Disconnect(SCARD.UNPOWER_CARD); | |
} | |
/**m* SCardChannel/DisconnectReset | |
* | |
* NAME | |
* SCardChannel.DisconnectReset() | |
* | |
* SYNOPSIS | |
* void DisconnectReset() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Disconnect(SCARD.RESET_CARD) | |
* | |
**/ | |
public void DisconnectReset() | |
{ | |
Disconnect(SCARD.RESET_CARD); | |
} | |
/**m* SCardChannel/DisconnectLeave | |
* | |
* NAME | |
* SCardChannel.DisconnectLeave() | |
* | |
* SYNOPSIS | |
* void DisconnectLeave() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Disconnect(SCARD.LEAVE_CARD) | |
* | |
**/ | |
public void DisconnectLeave() | |
{ | |
Disconnect(SCARD.LEAVE_CARD); | |
} | |
/**m* SCardChannel/Reconnect | |
* | |
* NAME | |
* SCardChannel.Reconnect() | |
* | |
* SYNOPSIS | |
* bool Reconnect() | |
* bool Reconnect(uint disposition) | |
* | |
* DESCRIPTION | |
* Re-open the connection channel to the smartcard | |
* | |
* INPUTS | |
* The disposition parameter must take one of the following values: | |
* - SCARD.EJECT_CARD | |
* - SCARD.UNPOWER_CARD | |
* - SCARD.RESET_CARD | |
* - SCARD.LEAVE_CARD | |
* If this parameter is omitted, it defaults to SCARD.RESET_CARD | |
* | |
* OUTPUT | |
* Returns true if the connection has been successfully re-established. | |
* Returns false if not. See LastError for details. | |
* | |
* SEE ALSO | |
* SCardChannel.Connect | |
* SCardChannel.Disconnect | |
* | |
**/ | |
public bool Reconnect(uint disposition) | |
{ | |
uint rc; | |
if (!Connected) | |
return false; | |
rc = | |
SCARD.Reconnect(_hCard, _share_mode, _want_protocols, disposition, ref _active_protocol); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_hCard = IntPtr.Zero; | |
_last_error = rc; | |
return false; | |
} | |
UpdateState(); | |
return true; | |
} | |
public void Reconnect() | |
{ | |
ReconnectReset(); | |
} | |
/**m* SCardChannel/ReconnectEject | |
* | |
* NAME | |
* SCardChannel.ReconnectEject() | |
* | |
* SYNOPSIS | |
* void ReconnectEject() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Reconnect(SCARD.EJECT_CARD) | |
* | |
**/ | |
public void ReconnectEject() | |
{ | |
Reconnect(SCARD.EJECT_CARD); | |
} | |
/**m* SCardChannel/ReconnectUnpower | |
* | |
* NAME | |
* SCardChannel.ReconnectUnpower() | |
* | |
* SYNOPSIS | |
* void ReconnectUnpower() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Reconnect(SCARD.UNPOWER_CARD) | |
* | |
**/ | |
public void ReconnectUnpower() | |
{ | |
Reconnect(SCARD.UNPOWER_CARD); | |
} | |
/**m* SCardChannel/ReconnectReset | |
* | |
* NAME | |
* SCardChannel.ReconnectReset() | |
* | |
* SYNOPSIS | |
* void ReconnectReset() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Disconnect(SCARD.RESET_CARD) | |
* | |
**/ | |
public void ReconnectReset() | |
{ | |
Reconnect(SCARD.RESET_CARD); | |
} | |
/**m* SCardChannel/ReconnectLeave | |
* | |
* NAME | |
* SCardChannel.ReconnectLeave() | |
* | |
* SYNOPSIS | |
* void ReconnectLeave() | |
* | |
* DESCRIPTION | |
* Same as SCardChannel.Reconnect(SCARD.LEAVE_CARD) | |
* | |
**/ | |
public void ReconnectLeave() | |
{ | |
Reconnect(SCARD.LEAVE_CARD); | |
} | |
/**m* SCardChannel/Transmit | |
* | |
* NAME | |
* SCardChannel.Transmit() | |
* | |
* SYNOPSIS | |
* bool Transmit() | |
* RAPDU Transmit(CAPDU capdu) | |
* bool Transmit(CAPDU capdu, ref RAPDU rapdu) | |
* void Transmit(CAPDU capdu, TransmitDoneCallback callback) | |
* | |
* DESCRIPTION | |
* Sends a command APDU (CAPDU) to the connected card, and retrieves its response APDU (RAPDU) | |
* | |
* SOURCE | |
* | |
* SCardChannel card = new SCardChannel( ... reader ... ); | |
* if (!card.Connect( SCARD.PROTOCOL_T0|SCARD.PROTOCOL_T1 )) | |
* { | |
* // handle error | |
* } | |
* | |
* | |
* // Example 1 | |
* // --------- | |
* | |
* card.Command = new CAPDU("00 A4 00 00 02 3F 00"); | |
* if (!card.Transmit()) | |
* { | |
* // handle error | |
* } | |
* MessageBox.Show("Card answered: " + card.Response.AsString(" ")); | |
* | |
* | |
* // Example 2 | |
* // --------- | |
* | |
* RAPDU response = card.Transmit(new CAPDU("00 A4 00 00 02 3F 00"))) | |
* if (response == null) | |
* { | |
* // handle error | |
* } | |
* MessageBox.Show("Card answered: " + response.AsString(" ")); | |
* | |
* | |
* // Example 3 | |
* // --------- | |
* | |
* CAPDU command = new CAPDU("00 A4 00 00 02 3F 00"); | |
* RAPDU response = new RAPDU(); | |
* if (!card.Transmit(command, ref response)) | |
* { | |
* // handle error | |
* } | |
* MessageBox.Show("Card answered: " + response.AsString(" ")); | |
* | |
* | |
* // Example 4 | |
* // --------- | |
* | |
* // In this example the Transmit is performed by a background thread | |
* // We supply a delegate so the main class (window/form) will be notified | |
* // when Transmit will return | |
* | |
* delegate void OnTransmitDoneInvoker(RAPDU response); | |
* | |
* void OnTransmitDone(RAPDU response) | |
* { | |
* // Ensure we're back in the context of the main thread (application's message pump) | |
* if (this.InvokeRequired) | |
* { | |
* this.BeginInvoke(new OnTransmitDoneInvoker(OnTransmitDone), response); | |
* return; | |
* } | |
* | |
* if (response == null) | |
* { | |
* // handle error | |
* } | |
* | |
* MessageBox.Show("Card answered: " + response.AsString(" ")); | |
* } | |
* | |
* card.Transmit(new CAPDU("00 A4 00 00 02 3F 00"), new SCardChannel.TransmitDoneCallback(OnTransmitDone)); | |
* | |
* | |
* SEE ALSO | |
* SCardChannel.Connect | |
* SCardChannel.Transmit | |
* SCardChannel.Command | |
* SCardChannel.Response | |
* | |
**/ | |
public bool Transmit() | |
{ | |
byte[] rsp_buffer = new byte[258]; | |
uint rsp_length = 258; | |
uint rc; | |
_rapdu = null; | |
rc = SCARD.Transmit(_hCard, | |
IntPtr.Zero, | |
_capdu.Bytes, | |
_capdu.Size, | |
IntPtr.Zero, rsp_buffer, ref rsp_length); | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_last_error = rc; | |
return false; | |
} | |
_rapdu = new RAPDU(rsp_buffer, (int)rsp_length); | |
return true; | |
} | |
public bool Transmit(CAPDU capdu, ref RAPDU rapdu) | |
{ | |
_capdu = capdu; | |
if (!Transmit()) | |
return false; | |
rapdu = _rapdu; | |
return true; | |
} | |
public RAPDU Transmit(CAPDU capdu) | |
{ | |
_capdu = capdu; | |
if (!Transmit()) | |
return null; | |
return _rapdu; | |
} | |
public void Transmit(CAPDU capdu, TransmitDoneCallback callback) | |
{ | |
if (_transmit_thread != null) | |
_transmit_thread = null; | |
_capdu = capdu; | |
if (callback != null) | |
{ | |
_transmit_done_callback = callback; | |
_transmit_thread = new Thread(TransmitFunction); | |
_transmit_thread.Start(); | |
} | |
} | |
private void TransmitFunction() | |
{ | |
if (Transmit()) | |
{ | |
if (_transmit_done_callback != null) | |
_transmit_done_callback(_rapdu); | |
} | |
else | |
{ | |
if (_transmit_done_callback != null) | |
_transmit_done_callback(null); | |
} | |
} | |
public bool Control() | |
{ | |
byte[] rsp_buffer = new byte[258]; | |
uint rsp_length = 0; | |
uint rc; | |
_rctrl = null; | |
rc = SCARD.Control(_hCard, | |
SCARD.IOCTL_CSB6_PCSC_ESCAPE, | |
_cctrl.Bytes, | |
_cctrl.Size, rsp_buffer, 258, ref rsp_length); | |
if (rc == 1) | |
{ | |
rc = SCARD.Control(_hCard, | |
SCARD.IOCTL_MS_CCID_ESCAPE, | |
_cctrl.Bytes, | |
_cctrl.Size, rsp_buffer, 258, ref rsp_length); | |
} | |
if (rc != SCARD.S_SUCCESS) | |
{ | |
_last_error = rc; | |
return false; | |
} | |
_rctrl = new CardBuffer(rsp_buffer, (int)rsp_length); | |
return true; | |
} | |
public bool Control(CardBuffer cctrl, ref CardBuffer rctrl) | |
{ | |
_cctrl = cctrl; | |
if (!Control()) | |
return false; | |
rctrl = _rctrl; | |
return true; | |
} | |
public CardBuffer Control(CardBuffer cctrl) | |
{ | |
_cctrl = cctrl; | |
if (!Control()) | |
return null; | |
return _rctrl; | |
} | |
public bool Leds(byte red, byte green, byte blue) | |
{ | |
byte[] buffer = new byte[5]; | |
buffer[0] = 0x58; | |
buffer[1] = 0x1E; | |
buffer[2] = red; | |
buffer[3] = green; | |
buffer[4] = blue; | |
if (Control(new CardBuffer(buffer)) != null) | |
return true; | |
return false; | |
} | |
public bool Buzzer(ushort duration_ms) | |
{ | |
byte[] buffer = new byte[4]; | |
buffer[0] = 0x58; | |
buffer[1] = 0x1C; | |
buffer[2] = (byte)(duration_ms / 0x0100); | |
buffer[3] = (byte)(duration_ms % 0x0100); | |
if (Control(new CardBuffer(buffer)) != null) | |
return true; | |
return false; | |
} | |
/**v* SCardChannel/Command | |
* | |
* NAME | |
* SCardChannel.Command | |
* | |
* SYNOPSIS | |
* CAPDU Command | |
* | |
* DESCRIPTION | |
* C-APDU to be sent to the card through SCardChannel.Transmit | |
* | |
**/ | |
public CAPDU Command | |
{ | |
get | |
{ | |
return _capdu; | |
} | |
set | |
{ | |
_capdu = value; | |
} | |
} | |
/**v* SCardChannel/Response | |
* | |
* NAME | |
* SCardChannel.Response | |
* | |
* SYNOPSIS | |
* RAPDU Response | |
* | |
* DESCRIPTION | |
* R-APDU returned by the card after a succesfull call to SCardChannel.Transmit | |
* | |
**/ | |
public RAPDU Response | |
{ | |
get | |
{ | |
return _rapdu; | |
} | |
} | |
/**v* SCardChannel/LastError | |
* | |
* NAME | |
* SCardChannel.LastError | |
* | |
* SYNOPSIS | |
* uint LastError | |
* | |
* OUTPUT | |
* Returns the last error encountered by the object when working with SCARD functions. | |
* | |
* SEE ALSO | |
* SCardChannel.LastErrorAsString | |
* | |
**/ | |
public uint LastError | |
{ | |
get | |
{ | |
return _last_error; | |
} | |
} | |
/**v* SCardChannel/LastErrorAsString | |
* | |
* NAME | |
* SCardChannel.LastErrorAsString | |
* | |
* SYNOPSIS | |
* string LastErrorAsString | |
* | |
* OUTPUT | |
* Returns the last error encountered by the object when working with SCARD functions. | |
* | |
* SEE ALSO | |
* SCardChannel.LastError | |
* | |
**/ | |
public string LastErrorAsString | |
{ | |
get | |
{ | |
return SCARD.ErrorToString(_last_error); | |
} | |
} | |
} | |
#endregion | |
#region SCardBuffer class | |
/**c* SpringCardPCSC/CardBuffer | |
* | |
* NAME | |
* CardBuffer | |
* | |
* DESCRIPTION | |
* The CardBuffer provides convenient access to byte-arrays | |
* | |
* DERIVED BY | |
* CAPDU | |
* RAPDU | |
* | |
**/ | |
public class CardBuffer | |
{ | |
protected byte[] _bytes = null; | |
private bool isb(char c) | |
{ | |
bool r = false; | |
if ((c >= '0') && (c <= '9')) | |
{ | |
r = true; | |
} | |
else if ((c >= 'A') && (c <= 'F')) | |
{ | |
r = true; | |
} | |
else if ((c >= 'a') && (c <= 'f')) | |
{ | |
r = true; | |
} | |
return r; | |
} | |
private byte htob(char c) | |
{ | |
int r = 0; | |
if ((c >= '0') && (c <= '9')) | |
{ | |
r = c - '0'; | |
} | |
else if ((c >= 'A') && (c <= 'F')) | |
{ | |
r = c - 'A'; | |
r += 10; | |
} | |
else if ((c >= 'a') && (c <= 'f')) | |
{ | |
r = c - 'a'; | |
r += 10; | |
} | |
return (byte)r; | |
} | |
public CardBuffer() | |
{ | |
} | |
public CardBuffer(byte[] bytes) | |
{ | |
_bytes = bytes; | |
} | |
public CardBuffer(byte[] bytes, int length) | |
{ | |
SetBytes(bytes, length); | |
} | |
public CardBuffer(byte[] bytes, int offset, int length) | |
{ | |
SetBytes(bytes, offset, length); | |
} | |
public CardBuffer(string str) | |
{ | |
SetString(str); | |
} | |
public void SetBytes(byte[] bytes) | |
{ | |
_bytes = bytes; | |
} | |
public void SetBytes(byte[] bytes, int length) | |
{ | |
_bytes = new byte[length]; | |
int i; | |
for (i = 0; i < length; i++) | |
_bytes[i] = bytes[i]; | |
} | |
public void SetBytes(byte[] bytes, int length, int offset) | |
{ | |
_bytes = new byte[length]; | |
int i; | |
for (i = 0; i < length; i++) | |
_bytes[i] = bytes[offset + i]; | |
} | |
public void SetString(string str) | |
{ | |
string s = ""; | |
int i, l; | |
l = str.Length; | |
for (i = 0; i < l; i++) | |
{ | |
char c = str[i]; | |
if (isb(c)) | |
s = s + c; | |
} | |
l = s.Length; | |
_bytes = new Byte[l / 2]; | |
for (i = 0; i < l; i += 2) | |
{ | |
_bytes[i / 2] = htob(s[i]); | |
_bytes[i / 2] *= 0x10; | |
_bytes[i / 2] += htob(s[i + 1]); | |
} | |
} | |
public string AsString(string separator) | |
{ | |
string s = ""; | |
int i; | |
if (_bytes != null) | |
{ | |
for (i = 0; i < _bytes.Length; i++) | |
{ | |
if (i > 0) | |
s = s + separator; | |
s = s + String.Format("{0:X02}", _bytes[i]); | |
} | |
} | |
return s; | |
} | |
public string AsString() | |
{ | |
return AsString(""); | |
} | |
public byte[] Bytes | |
{ | |
get | |
{ | |
return _bytes; | |
} | |
set | |
{ | |
_bytes = value; | |
} | |
} | |
public uint Size | |
{ | |
get | |
{ | |
return (uint)_bytes.Length; | |
} | |
} | |
} | |
#endregion | |
#region CAPDU class | |
/**c* SpringCardPCSC/CAPDU | |
* | |
* NAME | |
* CAPDU | |
* | |
* DESCRIPTION | |
* The CAPDU object is used to format and send COMMAND APDUs (according to ISO 7816-4) to the smartcard | |
* | |
* DERIVED FROM | |
* CardBuffer | |
* | |
**/ | |
public class CAPDU : CardBuffer | |
{ | |
byte _cla; | |
byte _ins; | |
byte _p1; | |
byte _p2; | |
byte _le; | |
bool _le_defined; | |
CardBuffer _data; | |
private void encode_apdu() | |
{ | |
uint l = 4; | |
if (_data != null) | |
{ | |
l++; | |
l += _data.Size; | |
} | |
if (_le_defined) | |
l++; | |
_bytes = new byte[l]; | |
_bytes[0] = _cla; | |
_bytes[1] = _ins; | |
_bytes[2] = _p1; | |
_bytes[3] = _p2; | |
if (_data != null) | |
{ | |
int i; | |
_bytes[4] = (byte)data.Size; | |
for (i = 0; i < data.Size; i++) | |
_bytes[5 + i] = data.Bytes[i]; | |
} | |
if (_le_defined) | |
{ | |
_bytes[l - 1] = _le; | |
} | |
} | |
private void decode_apdu() | |
{ | |
if (_bytes == null) | |
return; | |
if (_bytes.Length < 4) | |
return; | |
_cla = _bytes[0]; | |
_ins = _bytes[1]; | |
_p1 = _bytes[2]; | |
_p2 = _bytes[3]; | |
if (_bytes.Length == 4) | |
{ | |
_data = null; | |
_le_defined = false; | |
_le = 0; | |
} | |
else if (_bytes.Length == 5) | |
{ | |
_data = null; | |
_le_defined = true; | |
_le = _bytes[4]; | |
} | |
else | |
{ | |
if (_bytes.Length == (5 + _bytes[4])) | |
{ | |
_le_defined = false; | |
_le = 0; | |
} | |
else if (_bytes.Length == (6 + _bytes[4])) | |
{ | |
_le_defined = true; | |
_le = _bytes[_bytes.Length - 1]; | |
} | |
else | |
{ | |
return; | |
} | |
_data = new CardBuffer(_bytes, 5, _bytes[4]); | |
} | |
} | |
public CAPDU() | |
{ | |
} | |
public CAPDU(byte[] bytes) | |
{ | |
_bytes = bytes; | |
decode_apdu(); | |
} | |
public CAPDU(byte CLA, byte INS, byte P1, byte P2) | |
{ | |
_bytes = new byte[4]; | |
_bytes[0] = CLA; | |
_bytes[1] = INS; | |
_bytes[2] = P1; | |
_bytes[3] = P2; | |
decode_apdu(); | |
} | |
public CAPDU(byte CLA, byte INS, byte P1, byte P2, byte P3) | |
{ | |
_bytes = new byte[5]; | |
_bytes[0] = CLA; | |
_bytes[1] = INS; | |
_bytes[2] = P1; | |
_bytes[3] = P2; | |
_bytes[4] = P3; | |
decode_apdu(); | |
} | |
public CAPDU(byte CLA, byte INS, byte P1, byte P2, byte[] data) | |
{ | |
int i; | |
_bytes = new byte[5 + data.Length]; | |
_bytes[0] = CLA; | |
_bytes[1] = INS; | |
_bytes[2] = P1; | |
_bytes[3] = P2; | |
_bytes[4] = (byte)data.Length; | |
for (i = 0; i < data.Length; i++) | |
_bytes[5 + i] = data[i]; | |
decode_apdu(); | |
} | |
public CAPDU(byte CLA, byte INS, byte P1, byte P2, string data) | |
{ | |
int i; | |
byte[] _data = (new CardBuffer(data)).Bytes; | |
_bytes = new byte[5 + _data.Length]; | |
_bytes[0] = CLA; | |
_bytes[1] = INS; | |
_bytes[2] = P1; | |
_bytes[3] = P2; | |
_bytes[4] = (byte)_data.Length; | |
for (i = 0; i < _data.Length; i++) | |
_bytes[5 + i] = _data[i]; | |
decode_apdu(); | |
} | |
public CAPDU(byte CLA, byte INS, byte P1, byte P2, byte[] data, byte LE) | |
{ | |
int i; | |
_bytes = new byte[6 + data.Length]; | |
_bytes[0] = CLA; | |
_bytes[1] = INS; | |
_bytes[2] = P1; | |
_bytes[3] = P2; | |
_bytes[4] = (byte)data.Length; | |
for (i = 0; i < data.Length; i++) | |
_bytes[5 + i] = data[i]; | |
_bytes[5 + data.Length] = LE; | |
decode_apdu(); | |
} | |
public CAPDU(byte CLA, byte INS, byte P1, byte P2, string data, byte LE) | |
{ | |
int i; | |
byte[] _data = (new CardBuffer(data)).Bytes; | |
_bytes = new byte[6 + _data.Length]; | |
_bytes[0] = CLA; | |
_bytes[1] = INS; | |
_bytes[2] = P1; | |
_bytes[3] = P2; | |
_bytes[4] = (byte)_data.Length; | |
for (i = 0; i < _data.Length; i++) | |
_bytes[5 + i] = _data[i]; | |
_bytes[5 + _data.Length] = LE; | |
decode_apdu(); | |
} | |
public CAPDU(string str) | |
{ | |
SetString(str); | |
decode_apdu(); | |
} | |
public byte CLA | |
{ | |
get | |
{ | |
return _cla; | |
} | |
set | |
{ | |
_cla = value; | |
encode_apdu(); | |
} | |
} | |
public byte INS | |
{ | |
get | |
{ | |
return _ins; | |
} | |
set | |
{ | |
_ins = value; | |
encode_apdu(); | |
} | |
} | |
public byte P1 | |
{ | |
get | |
{ | |
return _p1; | |
} | |
set | |
{ | |
_p1 = value; | |
encode_apdu(); | |
} | |
} | |
public byte P2 | |
{ | |
get | |
{ | |
return _p2; | |
} | |
set | |
{ | |
_p2 = value; | |
encode_apdu(); | |
} | |
} | |
public byte Lc | |
{ | |
get | |
{ | |
if (data == null) | |
return 0; | |
if (data.Size > 255) | |
return 0; | |
return (byte)data.Size; | |
} | |
} | |
public byte Le | |
{ | |
get | |
{ | |
return _le; | |
} | |
set | |
{ | |
_le = value; | |
_le_defined = true; | |
encode_apdu(); | |
} | |
} | |
public CardBuffer data | |
{ | |
get | |
{ | |
return _data; | |
} | |
set | |
{ | |
if ((value != null) && (value.Size < 256)) | |
{ | |
_data = value; | |
encode_apdu(); | |
} | |
} | |
} | |
} | |
#endregion | |
#region RAPDU class | |
/**c* SpringCardPCSC/RAPDU | |
* | |
* NAME | |
* RAPDU | |
* | |
* DESCRIPTION | |
* The RAPDU object is used to receive and decode RESPONSE APDUs (according to ISO 7816-4) from the smartcard | |
* | |
* DERIVED FROM | |
* CardBuffer | |
* | |
**/ | |
public class RAPDU : CardBuffer | |
{ | |
public bool isValid | |
{ | |
get | |
{ | |
return (_bytes.Length >= 2); | |
} | |
} | |
public RAPDU(byte[] bytes, int length) | |
{ | |
SetBytes(bytes, length); | |
} | |
public bool hasData | |
{ | |
get | |
{ | |
return (_bytes.Length > 2); | |
} | |
} | |
public CardBuffer data | |
{ | |
get | |
{ | |
return new CardBuffer(_bytes, _bytes.Length - 2); | |
} | |
} | |
public byte SW1 | |
{ | |
get | |
{ | |
return _bytes[_bytes.Length - 2]; | |
} | |
} | |
public byte SW2 | |
{ | |
get | |
{ | |
return _bytes[_bytes.Length - 1]; | |
} | |
} | |
public ushort SW | |
{ | |
get | |
{ | |
ushort r; | |
r = _bytes[_bytes.Length - 2]; | |
r *= 0x0100; | |
r += _bytes[_bytes.Length - 1]; | |
return r; | |
} | |
} | |
public string SWString | |
{ | |
get | |
{ | |
return SCARD.CardStatusWordsToString(SW1, SW2); | |
} | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment