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:
Henryk Plötz 2010-02-25 16:43:54 +01:00
parent 63d7c9d0a8
commit 331bfc4d47
4 changed files with 151 additions and 99 deletions

View File

@ -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

View File

@ -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)

130
readers.py Normal file
View File

@ -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()

View File

@ -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) )