API change: substitute new PURPOSE_GET_RESPONSE for PURPOSE_RETRY to match the actual semantics, add a different value for PURPOSE_RETRY to match the description
Refactor READ BINARY functionality out of iso_7816_4 into a building block Implement READ BINARY (and "cat" command) for RFID storage cards using the new building block git-svn-id: svn+ssh://localhost/home/henryk/svn/cyberflex-shell/trunk@207 f711b948-2313-0410-aaa9-d29f33439f0b
This commit is contained in:
parent
85335da1b6
commit
62a06241c0
|
@ -93,3 +93,43 @@ class Card_with_80_aa(Card_with_ls):
|
|||
"List EFs in current DF"
|
||||
result = self.list_x(2)
|
||||
print "EFs: " + ", ".join([utils.hexdump(a, short=True) for a in result])
|
||||
|
||||
class Card_with_read_binary:
|
||||
DATA_UNIT_SIZE=1
|
||||
|
||||
def read_binary_file(self, offset = 0):
|
||||
"""Read from the currently selected EF.
|
||||
Repeat calls to READ BINARY as necessary to get the whole EF."""
|
||||
|
||||
if offset >= 1<<15:
|
||||
raise ValueError, "offset is limited to 15 bits"
|
||||
contents = ""
|
||||
had_one = False
|
||||
|
||||
while True:
|
||||
command = C_APDU(self.APDU_READ_BINARY, p1 = offset >> 8, p2 = (offset & 0xff))
|
||||
result = self.send_apdu(command)
|
||||
if len(result.data) > 0:
|
||||
contents = contents + result.data
|
||||
offset = offset + (len(result.data) / self.DATA_UNIT_SIZE)
|
||||
|
||||
if not self.check_sw(result.sw):
|
||||
break
|
||||
else:
|
||||
had_one = True
|
||||
|
||||
if had_one: ## If there was at least one successful pass, ignore any error SW. It probably only means "end of file"
|
||||
self.sw_changed = False
|
||||
|
||||
return contents
|
||||
|
||||
def cmd_cat(self):
|
||||
"Print a hexdump of the currently selected file (e.g. consecutive READ BINARY)"
|
||||
contents = self.read_binary_file()
|
||||
self.last_result = R_APDU(contents + self.last_sw)
|
||||
print utils.hexdump(contents)
|
||||
|
||||
COMMANDS = {
|
||||
"cat": cmd_cat,
|
||||
}
|
||||
|
||||
|
|
|
@ -16,20 +16,23 @@ DEBUG = True
|
|||
|
||||
## Constants for check_sw()
|
||||
PURPOSE_SUCCESS = 1 # Command executed successful
|
||||
PURPOSE_RETRY = 2 # Command executed successful but needs retry with correct length
|
||||
PURPOSE_GET_RESPONSE = 2 # Command executed successful but needs GET RESPONSE with correct length
|
||||
PURPOSE_SM_OK = 3 # Command not executed successful or with warnings, but response still contains SM objects
|
||||
PURPOSE_RETRY = 4 # Command would be executed successful but needs retry with correct length
|
||||
|
||||
_GENERIC_NAME = "Generic"
|
||||
class Card:
|
||||
DRIVER_NAME = [_GENERIC_NAME]
|
||||
APDU_GET_RESPONSE = C_APDU(ins=0xc0)
|
||||
APDU_VERIFY_PIN = C_APDU(ins=0x20)
|
||||
PURPOSE_SUCCESS, PURPOSE_RETRY, PURPOSE_SM_OK = PURPOSE_SUCCESS, PURPOSE_RETRY, PURPOSE_SM_OK
|
||||
PURPOSE_SUCCESS, PURPOSE_GET_RESPONSE, PURPOSE_SM_OK, PURPOSE_RETRY = PURPOSE_SUCCESS, PURPOSE_GET_RESPONSE, PURPOSE_SM_OK, PURPOSE_RETRY
|
||||
## Map for check_sw()
|
||||
STATUS_MAP = {
|
||||
PURPOSE_SUCCESS: ("\x90\x00", ),
|
||||
PURPOSE_RETRY: ("61??", ), ## If this is received then GET RESPONSE should be called with SW2
|
||||
PURPOSE_SM_OK: ("\x90\x00",)
|
||||
PURPOSE_GET_RESPONSE: ("61??", ), ## If this is received then GET RESPONSE should be called with SW2
|
||||
PURPOSE_SM_OK: ("\x90\x00",),
|
||||
PURPOSE_RETRY: (), ## Theoretically this would contain "6C??", but I dare not automatically resending a command for _all_ card types
|
||||
## Instead, card types for which this is safe should set it in their own STATUS_MAP
|
||||
}
|
||||
## Note: an item in this list must be a tuple of (atr, mask) where atr is a binary
|
||||
## string and mask a binary mask. Alternatively mask may be None, then ATR must be a regex
|
||||
|
@ -86,6 +89,7 @@ class Card:
|
|||
"\xD2\x76\x00\x00\x05": ("Giesecke & Devrient", ),
|
||||
"\xD2\x76\x00\x00\x40": ("Zentralinstitut fuer die Kassenaerztliche Versorgung in der Bundesrepublik Deutschland", ), # hpc-use-cases-01.pdf
|
||||
"\xa0\x00\x00\x02\x47": ("ICAO", ),
|
||||
"\xa0\x00\x00\x03\x06": ("PC/SC Workgroup", ),
|
||||
}
|
||||
|
||||
def _decode_df_name(self, value):
|
||||
|
@ -216,10 +220,14 @@ class Card:
|
|||
def _send_with_retry(self, apdu):
|
||||
result = self._real_send(apdu)
|
||||
|
||||
if self.check_sw(result.sw, PURPOSE_RETRY):
|
||||
if self.check_sw(result.sw, PURPOSE_GET_RESPONSE):
|
||||
## Need to call GetResponse
|
||||
gr_apdu = C_APDU(self.APDU_GET_RESPONSE, le = result.sw2) # FIXME
|
||||
result = R_APDU(self._real_send(gr_apdu))
|
||||
elif self.check_sw(result.sw, PURPOSE_RETRY) and apdu.Le == 0:
|
||||
## Retry with correct Le
|
||||
gr_apdu = C_APDU(apdu, le = result.sw2)
|
||||
result = R_APDU(self._real_send(gr_apdu))
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class GSM_Card(Card):
|
|||
DRIVER_NAME = ["GSM"]
|
||||
APDU_GET_RESPONSE = C_APDU("\xa0\xC0\x00\x00")
|
||||
STATUS_MAP = {
|
||||
PURPOSE_RETRY: ("9F??", )
|
||||
PURPOSE_GET_RESPONSE: ("9F??", )
|
||||
}
|
||||
|
||||
ATRS = [
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import TLV_utils
|
||||
from generic_card import *
|
||||
from generic_application import Application
|
||||
import building_blocks
|
||||
|
||||
class ISO_7816_4_Card(Card):
|
||||
class ISO_7816_4_Card(building_blocks.Card_with_read_binary,Card):
|
||||
APDU_SELECT_APPLICATION = C_APDU(ins=0xa4,p1=0x04)
|
||||
APDU_SELECT_FILE = C_APDU(ins=0xa4, le=0)
|
||||
APDU_READ_BINARY = C_APDU(ins=0xb0,le=0)
|
||||
|
@ -56,32 +57,6 @@ class ISO_7816_4_Card(Card):
|
|||
if len(result.data) > 0:
|
||||
print utils.hexdump(result.data)
|
||||
print TLV_utils.decode(result.data,tags=self.TLV_OBJECTS)
|
||||
|
||||
def read_binary_file(self, offset = 0):
|
||||
"""Read from the currently selected EF.
|
||||
Repeat calls to READ BINARY as necessary to get the whole EF."""
|
||||
|
||||
if offset >= 1<<15:
|
||||
raise ValueError, "offset is limited to 15 bits"
|
||||
contents = ""
|
||||
had_one = False
|
||||
|
||||
while True:
|
||||
command = C_APDU(self.APDU_READ_BINARY, p1 = offset >> 8, p2 = (offset & 0xff))
|
||||
result = self.send_apdu(command)
|
||||
if len(result.data) > 0:
|
||||
contents = contents + result.data
|
||||
offset = offset + len(result.data)
|
||||
|
||||
if not self.check_sw(result.sw):
|
||||
break
|
||||
else:
|
||||
had_one = True
|
||||
|
||||
if had_one: ## If there was at least one successful pass, ignore any error SW. It probably only means "end of file"
|
||||
self.sw_changed = False
|
||||
|
||||
return contents
|
||||
|
||||
def read_record(self, p1 = 0, p2 = 0, le = 0):
|
||||
"Read a record from the currently selected file"
|
||||
|
@ -89,12 +64,6 @@ class ISO_7816_4_Card(Card):
|
|||
result = self.send_apdu(command)
|
||||
return result.data
|
||||
|
||||
def cmd_cat(self):
|
||||
"Print a hexdump of the currently selected file (e.g. consecutive READ BINARY)"
|
||||
contents = self.read_binary_file()
|
||||
self.last_result = R_APDU(contents + self.last_sw)
|
||||
print utils.hexdump(contents)
|
||||
|
||||
def cmd_read_record(self, p1 = None, p2 = None, le = "0"):
|
||||
"Read a record"
|
||||
if p1 is None and p2 is None:
|
||||
|
@ -163,11 +132,11 @@ class ISO_7816_4_Card(Card):
|
|||
] )
|
||||
|
||||
COMMANDS = dict(Card.COMMANDS)
|
||||
COMMANDS.update(building_blocks.Card_with_read_binary.COMMANDS)
|
||||
COMMANDS.update( {
|
||||
"select_application": cmd_selectapplication,
|
||||
"select_file": cmd_selectfile,
|
||||
"cd": cmd_cd,
|
||||
"cat": cmd_cat,
|
||||
"open": cmd_open,
|
||||
"read_record": cmd_read_record,
|
||||
"next_record": cmd_next_record,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import utils
|
||||
from generic_card import *
|
||||
import building_blocks
|
||||
|
||||
class RFID_Card(Card):
|
||||
DRIVER_NAME = ["RFID"]
|
||||
|
@ -28,13 +29,29 @@ class RFID_Card(Card):
|
|||
COMMANDS = {
|
||||
"get_uid": cmd_get_uid,
|
||||
}
|
||||
|
||||
STATUS_WORDS = dict(Card.STATUS_WORDS)
|
||||
STATUS_WORDS.update( {
|
||||
"\x67\x00": "Wrong Length",
|
||||
"\x68\x00": "Class byte is not correct",
|
||||
"\x6a\x81": "Function not supported",
|
||||
"\x6b\x00": "Wrong parameters P1-P2",
|
||||
} )
|
||||
|
||||
class RFID_Storage_Card(RFID_Card):
|
||||
class RFID_Storage_Card(building_blocks.Card_with_read_binary,RFID_Card):
|
||||
STOP_ATRS = []
|
||||
ATRS = []
|
||||
STATUS_MAP = dict(RFID_Card.STATUS_MAP)
|
||||
STATUS_MAP.update( {
|
||||
Card.PURPOSE_RETRY: ("6C??", ),
|
||||
} )
|
||||
|
||||
APDU_READ_BINARY = utils.C_APDU(CLA=0xff, INS=0xb0, Le=0)
|
||||
COMMANDS = dict(building_blocks.Card_with_read_binary.COMMANDS)
|
||||
COMMANDS.update(RFID_Card.COMMANDS)
|
||||
|
||||
class Mifare_Card(RFID_Storage_Card):
|
||||
pass
|
||||
DATA_UNIT_SIZE=4
|
||||
|
||||
class Mifare_Classic_Card(Mifare_Card):
|
||||
DRIVER_NAME = ["Mifare Classic"]
|
||||
|
|
Loading…
Reference in New Issue