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]
|
||||
|
||||
def __init__(self, card):
|
||||
if not hasattr(card, "connection"):
|
||||
self.card = card
|
||||
else:
|
||||
self.card = card.connection
|
||||
self.cardservice = card
|
||||
def __init__(self, reader):
|
||||
self.reader = reader
|
||||
|
||||
self._i = 0
|
||||
self.last_apdu = None
|
||||
|
@ -198,19 +194,13 @@ class Card:
|
|||
"show_applications": cmd_show_applications,
|
||||
}
|
||||
|
||||
PROTOMAP = {
|
||||
0: smartcard.scard.SCARD_PCI_T0,
|
||||
1: smartcard.scard.SCARD_PCI_T1,
|
||||
}
|
||||
def _real_send(self, apdu):
|
||||
apdu_binary = apdu.render()
|
||||
|
||||
if DEBUG:
|
||||
print ">> " + utils.hexdump(apdu_binary, indent = 3)
|
||||
|
||||
apdu_bytes = map(lambda x: ord(x), 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_binary = self.reader.transceive(apdu_binary)
|
||||
result = R_APDU(result_binary)
|
||||
|
||||
self.last_apdu = apdu
|
||||
|
@ -258,20 +248,16 @@ class Card:
|
|||
if purpose is None: purpose = Card.PURPOSE_SUCCESS
|
||||
return self.match_statusword(self.STATUS_MAP[purpose], sw)
|
||||
|
||||
def _get_atr(card):
|
||||
if hasattr(card, "connection"):
|
||||
ATR = smartcard.util.toASCIIString(card.connection.getATR())
|
||||
else:
|
||||
ATR = smartcard.util.toASCIIString(card.getATR())
|
||||
return ATR
|
||||
def _get_atr(reader):
|
||||
return reader.get_ATR()
|
||||
_get_atr = staticmethod(_get_atr)
|
||||
|
||||
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."""
|
||||
ATR = cls._get_atr(card)
|
||||
ATR = cls._get_atr(reader)
|
||||
def match_list(atr, list):
|
||||
for (knownatr, mask) in list:
|
||||
if mask is None:
|
||||
|
@ -330,9 +316,6 @@ class Card:
|
|||
else:
|
||||
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):
|
||||
if len(self.DRIVER_NAME) > 1:
|
||||
|
@ -343,8 +326,6 @@ class Card:
|
|||
|
||||
def close_card(self):
|
||||
"Disconnect from a card"
|
||||
self.card.disconnect()
|
||||
if hasattr(self, "cardservice"):
|
||||
del self.cardservice
|
||||
del self.card
|
||||
self.reader.disconnect()
|
||||
del self.reader
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- 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
|
||||
|
||||
class Logger(object):
|
||||
|
@ -94,7 +94,8 @@ class Cyberflex_Shell(Shell):
|
|||
|
||||
def cmd_listreaders(self):
|
||||
"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):
|
||||
"Encrypt or decrypt with openssl-like interface"
|
||||
|
@ -156,7 +157,7 @@ class Cyberflex_Shell(Shell):
|
|||
|
||||
def cmd_atr(self, *args):
|
||||
"""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):
|
||||
"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
|
||||
print "Logging to %s" % filename
|
||||
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):
|
||||
raise
|
||||
except:
|
||||
|
@ -341,8 +342,8 @@ class Cyberflex_Shell(Shell):
|
|||
if reader is None:
|
||||
reader = self.reader
|
||||
|
||||
card_object = utils.CommandLineArgumentHelper.connect_to(reader)
|
||||
self.card = cards.new_card_object(card_object)
|
||||
reader_object = readers.connect_to(reader)
|
||||
self.card = cards.new_card_object(reader_object)
|
||||
|
||||
self.unregister_commands(self, self.NOCARD_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
|
||||
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
|
||||
import string, binascii, sys, re, getopt, readers
|
||||
|
||||
class CommandLineArgumentHelper:
|
||||
OPTIONS = "r:l"
|
||||
|
@ -15,65 +6,13 @@ class CommandLineArgumentHelper:
|
|||
exit_now = False
|
||||
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):
|
||||
"Open the connection to a card"
|
||||
|
||||
if self.reader is None:
|
||||
self.reader = 0
|
||||
|
||||
return self.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)
|
||||
return readers.connect_to(self.reader)
|
||||
|
||||
def getopt(self, argv, opts="", long_opts=[]):
|
||||
"Wrapper around getopt.gnu_getopt. Handles common arguments, returns everything else."
|
||||
|
@ -85,7 +24,8 @@ class CommandLineArgumentHelper:
|
|||
if option in ("-r","--reader"):
|
||||
self.reader = value
|
||||
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
|
||||
else:
|
||||
unrecognized.append( (option, value) )
|
||||
|
|
Loading…
Reference in New Issue