Move all PC/SC specific stuff to a separate class structure for readers (thus preparing the way for non-PC/SC-readers)
This commit should be known as "Holy excessive layering, batman!"
This commit is contained in:
parent
63d7c9d0a8
commit
331bfc4d47
|
@ -130,12 +130,8 @@ class Card:
|
||||||
}
|
}
|
||||||
TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]
|
TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]
|
||||||
|
|
||||||
def __init__(self, card):
|
def __init__(self, reader):
|
||||||
if not hasattr(card, "connection"):
|
self.reader = reader
|
||||||
self.card = card
|
|
||||||
else:
|
|
||||||
self.card = card.connection
|
|
||||||
self.cardservice = card
|
|
||||||
|
|
||||||
self._i = 0
|
self._i = 0
|
||||||
self.last_apdu = None
|
self.last_apdu = None
|
||||||
|
@ -198,19 +194,13 @@ class Card:
|
||||||
"show_applications": cmd_show_applications,
|
"show_applications": cmd_show_applications,
|
||||||
}
|
}
|
||||||
|
|
||||||
PROTOMAP = {
|
|
||||||
0: smartcard.scard.SCARD_PCI_T0,
|
|
||||||
1: smartcard.scard.SCARD_PCI_T1,
|
|
||||||
}
|
|
||||||
def _real_send(self, apdu):
|
def _real_send(self, apdu):
|
||||||
apdu_binary = apdu.render()
|
apdu_binary = apdu.render()
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print ">> " + utils.hexdump(apdu_binary, indent = 3)
|
print ">> " + utils.hexdump(apdu_binary, indent = 3)
|
||||||
|
|
||||||
apdu_bytes = map(lambda x: ord(x), apdu_binary)
|
result_binary = self.reader.transceive(apdu_binary)
|
||||||
data, sw1, sw2 = self.card.transmit(apdu_bytes, protocol=self.PROTOMAP[self.get_protocol()])
|
|
||||||
result_binary = map(lambda x: chr(x), data + [sw1,sw2])
|
|
||||||
result = R_APDU(result_binary)
|
result = R_APDU(result_binary)
|
||||||
|
|
||||||
self.last_apdu = apdu
|
self.last_apdu = apdu
|
||||||
|
@ -258,20 +248,16 @@ class Card:
|
||||||
if purpose is None: purpose = Card.PURPOSE_SUCCESS
|
if purpose is None: purpose = Card.PURPOSE_SUCCESS
|
||||||
return self.match_statusword(self.STATUS_MAP[purpose], sw)
|
return self.match_statusword(self.STATUS_MAP[purpose], sw)
|
||||||
|
|
||||||
def _get_atr(card):
|
def _get_atr(reader):
|
||||||
if hasattr(card, "connection"):
|
return reader.get_ATR()
|
||||||
ATR = smartcard.util.toASCIIString(card.connection.getATR())
|
|
||||||
else:
|
|
||||||
ATR = smartcard.util.toASCIIString(card.getATR())
|
|
||||||
return ATR
|
|
||||||
_get_atr = staticmethod(_get_atr)
|
_get_atr = staticmethod(_get_atr)
|
||||||
|
|
||||||
def get_atr(self):
|
def get_atr(self):
|
||||||
return self._get_atr(self.card)
|
return self._get_atr(self.reader)
|
||||||
|
|
||||||
def can_handle(cls, card):
|
def can_handle(cls, reader):
|
||||||
"""Determine whether this class can handle a given card/connection object."""
|
"""Determine whether this class can handle a given card/connection object."""
|
||||||
ATR = cls._get_atr(card)
|
ATR = cls._get_atr(reader)
|
||||||
def match_list(atr, list):
|
def match_list(atr, list):
|
||||||
for (knownatr, mask) in list:
|
for (knownatr, mask) in list:
|
||||||
if mask is None:
|
if mask is None:
|
||||||
|
@ -330,9 +316,6 @@ class Card:
|
||||||
else:
|
else:
|
||||||
return "%s (SW %s)" % (retval, binascii.b2a_hex(self.last_sw))
|
return "%s (SW %s)" % (retval, binascii.b2a_hex(self.last_sw))
|
||||||
|
|
||||||
def get_protocol(self):
|
|
||||||
hresult, reader, state, protocol, atr = smartcard.scard.SCardStatus( self.card.component.hcard )
|
|
||||||
return ((protocol == smartcard.scard.SCARD_PROTOCOL_T0) and (0,) or (1,))[0]
|
|
||||||
|
|
||||||
def get_driver_name(self):
|
def get_driver_name(self):
|
||||||
if len(self.DRIVER_NAME) > 1:
|
if len(self.DRIVER_NAME) > 1:
|
||||||
|
@ -343,8 +326,6 @@ class Card:
|
||||||
|
|
||||||
def close_card(self):
|
def close_card(self):
|
||||||
"Disconnect from a card"
|
"Disconnect from a card"
|
||||||
self.card.disconnect()
|
self.reader.disconnect()
|
||||||
if hasattr(self, "cardservice"):
|
del self.reader
|
||||||
del self.cardservice
|
|
||||||
del self.card
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: iso-8859-1 -*-
|
# -*- coding: iso-8859-1 -*-
|
||||||
|
|
||||||
import crypto_utils, utils, cards, os, re, binascii, sys, exceptions, traceback, getopt, datetime
|
import crypto_utils, utils, cards, readers, os, re, binascii, sys, exceptions, traceback, getopt, datetime
|
||||||
from shell import Shell
|
from shell import Shell
|
||||||
|
|
||||||
class Logger(object):
|
class Logger(object):
|
||||||
|
@ -94,7 +94,8 @@ class Cyberflex_Shell(Shell):
|
||||||
|
|
||||||
def cmd_listreaders(self):
|
def cmd_listreaders(self):
|
||||||
"List the available readers"
|
"List the available readers"
|
||||||
list_readers()
|
for i, (name, obj) in enumerate(readers.list_readers()):
|
||||||
|
print "%i: %s" % (i,name)
|
||||||
|
|
||||||
def cmd_enc(self, *args):
|
def cmd_enc(self, *args):
|
||||||
"Encrypt or decrypt with openssl-like interface"
|
"Encrypt or decrypt with openssl-like interface"
|
||||||
|
@ -156,7 +157,7 @@ class Cyberflex_Shell(Shell):
|
||||||
|
|
||||||
def cmd_atr(self, *args):
|
def cmd_atr(self, *args):
|
||||||
"""Print the ATR of the currently inserted card."""
|
"""Print the ATR of the currently inserted card."""
|
||||||
print "ATR: %s" % utils.hexdump(self.card.card.status()['ATR'], short=True)
|
print "ATR: %s" % utils.hexdump(self.card.reader.get_ATR(), short=True)
|
||||||
|
|
||||||
def cmd_save_response(self, file_name, start = None, end = None):
|
def cmd_save_response(self, file_name, start = None, end = None):
|
||||||
"Save the data in the last response to a file. start and end are optional"
|
"Save the data in the last response to a file. start and end are optional"
|
||||||
|
@ -268,7 +269,7 @@ class Cyberflex_Shell(Shell):
|
||||||
sys.stdout = self.logger
|
sys.stdout = self.logger
|
||||||
print "Logging to %s" % filename
|
print "Logging to %s" % filename
|
||||||
try:
|
try:
|
||||||
self.logger.println( "# ATR of currently inserted card is: %s" % utils.hexdump(self.card.get_atr(), short=True) )
|
self.logger.println( "# ATR of currently inserted card is: %s" % utils.hexdump(self.card.reader.get_ATR(), short=True) )
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
|
@ -341,8 +342,8 @@ class Cyberflex_Shell(Shell):
|
||||||
if reader is None:
|
if reader is None:
|
||||||
reader = self.reader
|
reader = self.reader
|
||||||
|
|
||||||
card_object = utils.CommandLineArgumentHelper.connect_to(reader)
|
reader_object = readers.connect_to(reader)
|
||||||
self.card = cards.new_card_object(card_object)
|
self.card = cards.new_card_object(reader_object)
|
||||||
|
|
||||||
self.unregister_commands(self, self.NOCARD_COMMANDS)
|
self.unregister_commands(self, self.NOCARD_COMMANDS)
|
||||||
self.register_commands(self, self.CARD_COMMANDS)
|
self.register_commands(self, self.CARD_COMMANDS)
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
try:
|
||||||
|
import smartcard, smartcard.CardRequest
|
||||||
|
except ImportError:
|
||||||
|
print >>sys.stderr, """Could not import smartcard module. Please install pyscard
|
||||||
|
from http://pyscard.sourceforge.net/
|
||||||
|
If you can't install pyscard and want to continue using
|
||||||
|
pycsc you'll need to downgrade to SVN revision 246.
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
class Smartcard_Reader(object):
|
||||||
|
def list_readers():
|
||||||
|
"Return a list of tuples: (reader name, implementing object)"
|
||||||
|
return []
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"Create a connection to this reader"
|
||||||
|
raise NotImplementedError, "Please implement in a sub-class"
|
||||||
|
|
||||||
|
def get_ATR(self):
|
||||||
|
"Get the ATR of the inserted card as a binary string"
|
||||||
|
raise NotImplementedError, "Please implement in a sub-class"
|
||||||
|
|
||||||
|
def transceive(self, data):
|
||||||
|
"Send a binary blob, receive a binary blob"
|
||||||
|
raise NotImplementedError, "Please implement in a sub-class"
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"Disconnect from the card and release all resources"
|
||||||
|
raise NotImplementedError, "Please implement in a sub-class"
|
||||||
|
|
||||||
|
class PCSC_Reader(Smartcard_Reader):
|
||||||
|
def __init__(self, reader):
|
||||||
|
self._reader = reader
|
||||||
|
self._name = str(reader)
|
||||||
|
self._cardservice = None
|
||||||
|
|
||||||
|
name = property(lambda self: self._name, None, None, "The human readable name of the reader")
|
||||||
|
|
||||||
|
def list_readers(cls):
|
||||||
|
return [ (str(r), cls(r)) for r in smartcard.System.readers() ]
|
||||||
|
list_readers = classmethod(list_readers)
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
unpatched = False
|
||||||
|
printed = False
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
if not unpatched:
|
||||||
|
cardrequest = smartcard.CardRequest.CardRequest( readers=[self._reader], timeout=0.1 )
|
||||||
|
else:
|
||||||
|
cardrequest = smartcard.CardRequest.CardRequest( readers=[self._reader], timeout=1 )
|
||||||
|
|
||||||
|
self._cardservice = cardrequest.waitforcard()
|
||||||
|
self._cardservice.connection.connect()
|
||||||
|
break
|
||||||
|
except TypeError:
|
||||||
|
unpatched = True
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
|
except smartcard.Exceptions.CardRequestException:
|
||||||
|
if sys.exc_info()[1].message.endswith("Command timeout."):
|
||||||
|
if not printed:
|
||||||
|
print "Please insert card ..."
|
||||||
|
printed = True
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
except smartcard.Exceptions.CardRequestTimeoutException:
|
||||||
|
if not printed:
|
||||||
|
print "Please insert card ..."
|
||||||
|
printed = True
|
||||||
|
except smartcard.Exceptions.NoCardException:
|
||||||
|
print "Card is mute or absent. Please retry."
|
||||||
|
|
||||||
|
def get_ATR(self):
|
||||||
|
return smartcard.util.toASCIIString(self._cardservice.connection.getATR())
|
||||||
|
|
||||||
|
def get_protocol(self):
|
||||||
|
hresult, reader, state, protocol, atr = smartcard.scard.SCardStatus( self._cardservice.connection.component.hcard )
|
||||||
|
return ((protocol == smartcard.scard.SCARD_PROTOCOL_T0) and (0,) or (1,))[0]
|
||||||
|
|
||||||
|
PROTOMAP = {
|
||||||
|
0: smartcard.scard.SCARD_PCI_T0,
|
||||||
|
1: smartcard.scard.SCARD_PCI_T1,
|
||||||
|
}
|
||||||
|
|
||||||
|
def transceive(self, data):
|
||||||
|
data_bytes = map(lambda x: ord(x), data)
|
||||||
|
data, sw1, sw2 = self._cardservice.connection.transmit(data_bytes, protocol=self.PROTOMAP[self.get_protocol()])
|
||||||
|
result_binary = map(lambda x: chr(x), data + [sw1,sw2])
|
||||||
|
return result_binary
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
self._cardservice.connection.disconnect()
|
||||||
|
del self._cardservice
|
||||||
|
self._cardservice = None
|
||||||
|
|
||||||
|
def list_readers():
|
||||||
|
"Collect readers from all known drivers"
|
||||||
|
readers = PCSC_Reader.list_readers()
|
||||||
|
return readers
|
||||||
|
|
||||||
|
def connect_to(reader):
|
||||||
|
"Open the connection to a reader"
|
||||||
|
|
||||||
|
readerObject = None
|
||||||
|
readers = list_readers()
|
||||||
|
|
||||||
|
if isinstance(reader, int) or reader.isdigit():
|
||||||
|
reader = int(reader)
|
||||||
|
readerObject = readers[reader][1]
|
||||||
|
else:
|
||||||
|
for i, name, obj in readers:
|
||||||
|
if str(name).startswith(reader):
|
||||||
|
readerObject = obj
|
||||||
|
|
||||||
|
if readerObject is None:
|
||||||
|
readerObject = readers[0][1]
|
||||||
|
|
||||||
|
print "Using reader: %s" % readerObject.name
|
||||||
|
|
||||||
|
readerObject.connect()
|
||||||
|
|
||||||
|
from utils import hexdump
|
||||||
|
|
||||||
|
print "ATR: %s" % hexdump(readerObject.get_ATR(), short = True)
|
||||||
|
return readerObject
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
list_readers()
|
||||||
|
|
68
utils.py
68
utils.py
|
@ -1,13 +1,4 @@
|
||||||
import string, binascii, sys, re, getopt
|
import string, binascii, sys, re, getopt, readers
|
||||||
try:
|
|
||||||
import smartcard, smartcard.CardRequest
|
|
||||||
except ImportError:
|
|
||||||
print >>sys.stderr, """Could not import smartcard module. Please install pyscard
|
|
||||||
from http://pyscard.sourceforge.net/
|
|
||||||
If you can't install pyscard and want to continue using
|
|
||||||
pycsc you'll need to downgrade to SVN revision 246.
|
|
||||||
"""
|
|
||||||
raise
|
|
||||||
|
|
||||||
class CommandLineArgumentHelper:
|
class CommandLineArgumentHelper:
|
||||||
OPTIONS = "r:l"
|
OPTIONS = "r:l"
|
||||||
|
@ -15,65 +6,13 @@ class CommandLineArgumentHelper:
|
||||||
exit_now = False
|
exit_now = False
|
||||||
reader = None
|
reader = None
|
||||||
|
|
||||||
def list_readers():
|
|
||||||
for index, name in enumerate(smartcard.System.readers()):
|
|
||||||
print "%i: %s" % (index, name)
|
|
||||||
list_readers = staticmethod(list_readers)
|
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"Open the connection to a card"
|
"Open the connection to a card"
|
||||||
|
|
||||||
if self.reader is None:
|
if self.reader is None:
|
||||||
self.reader = 0
|
self.reader = 0
|
||||||
|
|
||||||
return self.connect_to(self.reader)
|
return readers.connect_to(self.reader)
|
||||||
|
|
||||||
def connect_to(reader):
|
|
||||||
"Open the connection to a reader"
|
|
||||||
|
|
||||||
readerObject = None
|
|
||||||
|
|
||||||
if isinstance(reader, int) or reader.isdigit():
|
|
||||||
reader = int(reader)
|
|
||||||
readerObject = smartcard.System.readers()[reader]
|
|
||||||
else:
|
|
||||||
for r in smartcard.System.readers():
|
|
||||||
if str(r).startswith(reader):
|
|
||||||
readerObject = r
|
|
||||||
|
|
||||||
if readerObject is None:
|
|
||||||
readerObject = smartcard.System.readers()[0]
|
|
||||||
|
|
||||||
print "Using reader: %s" % readerObject
|
|
||||||
unpatched = False
|
|
||||||
printed = False
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
if not unpatched:
|
|
||||||
cardrequest = smartcard.CardRequest.CardRequest( readers=[readerObject], timeout=0.1 )
|
|
||||||
else:
|
|
||||||
cardrequest = smartcard.CardRequest.CardRequest( readers=[readerObject], timeout=1 )
|
|
||||||
|
|
||||||
cardservice = cardrequest.waitforcard()
|
|
||||||
cardservice.connection.connect()
|
|
||||||
break
|
|
||||||
except TypeError:
|
|
||||||
unpatched = True
|
|
||||||
except (KeyboardInterrupt, SystemExit):
|
|
||||||
raise
|
|
||||||
except smartcard.Exceptions.CardRequestException:
|
|
||||||
if sys.exc_info()[1].message.endswith("Command timeout."):
|
|
||||||
if not printed:
|
|
||||||
print "Please insert card ..."
|
|
||||||
printed = True
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
except smartcard.Exceptions.NoCardException:
|
|
||||||
print "Card is mute or absent. Please retry."
|
|
||||||
|
|
||||||
print "ATR: %s" % hexdump(smartcard.util.toASCIIString(cardservice.connection.getATR()), short = True)
|
|
||||||
return cardservice
|
|
||||||
connect_to = staticmethod(connect_to)
|
|
||||||
|
|
||||||
def getopt(self, argv, opts="", long_opts=[]):
|
def getopt(self, argv, opts="", long_opts=[]):
|
||||||
"Wrapper around getopt.gnu_getopt. Handles common arguments, returns everything else."
|
"Wrapper around getopt.gnu_getopt. Handles common arguments, returns everything else."
|
||||||
|
@ -85,7 +24,8 @@ class CommandLineArgumentHelper:
|
||||||
if option in ("-r","--reader"):
|
if option in ("-r","--reader"):
|
||||||
self.reader = value
|
self.reader = value
|
||||||
elif option in ("-l","--list-readers"):
|
elif option in ("-l","--list-readers"):
|
||||||
self.list_readers()
|
for i, (name, obj) in enumerate(readers.list_readers()):
|
||||||
|
print "%i: %s" % (i,name)
|
||||||
self.exit_now = True
|
self.exit_now = True
|
||||||
else:
|
else:
|
||||||
unrecognized.append( (option, value) )
|
unrecognized.append( (option, value) )
|
||||||
|
|
Loading…
Reference in New Issue