Merge branch 'desfire-native'
This commit is contained in:
commit
367b41a061
|
@ -5,26 +5,21 @@ from utils import C_APDU, R_APDU
|
|||
DEBUG = True
|
||||
#DEBUG = False
|
||||
|
||||
## Constants for check_sw()
|
||||
PURPOSE_SUCCESS = 1 # Command executed successful
|
||||
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_GET_RESPONSE, PURPOSE_SM_OK, PURPOSE_RETRY = PURPOSE_SUCCESS, PURPOSE_GET_RESPONSE, PURPOSE_SM_OK, PURPOSE_RETRY
|
||||
COMMAND_GET_RESPONSE = None
|
||||
|
||||
## Constants for check_sw()
|
||||
PURPOSE_SUCCESS = 1 # Command executed successful
|
||||
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
|
||||
|
||||
## Map for check_sw()
|
||||
STATUS_MAP = {
|
||||
PURPOSE_SUCCESS: ("\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
|
||||
}
|
||||
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
|
||||
## to match on the ATRs hexlify representation
|
||||
|
@ -32,18 +27,15 @@ class Card:
|
|||
## Note: A list of _not_ supported ATRs, overriding any possible match in ATRS. Matching
|
||||
## is done as for ATRS.
|
||||
STOP_ATRS = []
|
||||
## Note: a key in this dictionary may either be a two-byte string containing
|
||||
## a binary status word, or a four-byte string containing a hexadecimal
|
||||
|
||||
## Note: a key in this dictionary may either be a one- or two-byte string containing
|
||||
## a binary status word, or a two or four-byte string containing a hexadecimal
|
||||
## status word, possibly with ? characters marking variable nibbles.
|
||||
## Hexadecimal characters MUST be in uppercase. The values that four-byte
|
||||
## Hexadecimal characters MUST be in uppercase. The values that two- or four-byte
|
||||
## strings map to may be either format strings, that can make use of the
|
||||
## keyword substitutions for SW1 and SW2 or a callable accepting two arguments
|
||||
## (SW1, SW2) that returns a string.
|
||||
STATUS_WORDS = {
|
||||
'\x90\x00': "Normal execution",
|
||||
'61??': "%(SW2)i (0x%(SW2)02x) bytes of response data can be retrieved with GetResponse.",
|
||||
'6C??': "Bad value for LE, 0x%(SW2)02x is the correct value.",
|
||||
'63C?': lambda SW1,SW2: "The counter has reached the value '%i'" % (SW2%16)
|
||||
}
|
||||
## For the format of this dictionary of dictionaries see TLV_utils.tags
|
||||
TLV_OBJECTS = {}
|
||||
|
@ -51,44 +43,10 @@ class Card:
|
|||
|
||||
## Format: "AID (binary)": ("name", [optional: description, {more information}])
|
||||
APPLICATIONS = {
|
||||
"\xa0\x00\x00\x01\x67\x45\x53\x49\x47\x4e": ("DF.ESIGN", ),
|
||||
"\xa0\x00\x00\x00\x63\x50\x4b\x43\x53\x2d\x31\x35": ("DF_PKCS15", ),
|
||||
"\xD2\x76\x00\x01\x24\x01": ("DF_OpenPGP", "OpenPGP card", {"significant_length": 6} ),
|
||||
"\xa0\x00\x00\x02\x47\x10\x01": ("DF_LDS", "Machine Readable Travel Document", {"alias": ("mrtd",)}),
|
||||
## The following are from 0341a.pdf: BSI-DSZ-CC-0341-2006
|
||||
"\xD2\x76\x00\x00\x66\x01": ("DF_SIG", "Signature application", {"fid": "\xAB\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x5A\x41\x02\x00": ("ZA_MF_NEU", "Zusatzanwendungen", {"fid": "\xA7\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x45\x43\x02\x00": ("DF_EC_CASH_NEU", "ec-Cash", {"fid": "\xA1\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x45\x50\x02\x00": ("DF_BOERSE_NEU", "Geldkarte", {"fid": "\xA2\x00", "alias": ("geldkarte",)}),
|
||||
"\xD2\x76\x00\x00\x25\x47\x41\x01\x00": ("DF_GA_MAESTRO", "GA-Maestro", {"fid": "\xAC\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x54\x44\x01\x00": ("DF_TAN", "TAN-Anwendung", {"fid": "\xAC\x02"}),
|
||||
"\xD2\x76\x00\x00\x25\x4D\x01\x02\x00": ("DF_MARKTPLATZ_NEU", "Marktplatz", {"fid": "\xB0\x01"}),
|
||||
"\xD2\x76\x00\x00\x25\x46\x53\x02\x00": ("DF_FAHRSCHEIN_NEU", "Fahrschein", {"fid": "\xB0\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x48\x42\x02\x00": ("DF_BANKING_20" , "HBCI", {"fid": "\xA6\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x4E\x50\x01\x00": ("DF_NOTEPAD", "Notepad", {"fid": "\xA6\x10"}),
|
||||
|
||||
"\xd2\x76\x00\x00\x85\x01\x00": ("NFC_TYPE_4", "NFC NDEF Application on tag type 4", {"alias": ("nfc",)}, ),
|
||||
|
||||
# From TR-03110_v201_pdf.pdf
|
||||
"\xE8\x07\x04\x00\x7f\x00\x07\x03\x02": ("DF_eID", "eID application"),
|
||||
|
||||
"\xd2\x76\x00\x00\x25\x4b\x41\x4e\x4d\x30\x31\x00": ("VRS_TICKET", "VRS Ticket", {"fid": "\xad\x00", "alias": ("vrs",)}, ),
|
||||
"\xd2\x76\x00\x01\x35\x4b\x41\x4e\x4d\x30\x31\x00": ("VRS_TICKET", "VRS Ticket", {"fid": "\xad\x00",}, ),
|
||||
}
|
||||
# Alias for DF_BOERSE_NEU
|
||||
APPLICATIONS["\xA0\x00\x00\x00\x59\x50\x41\x43\x45\x01\x00"] = APPLICATIONS["\xD2\x76\x00\x00\x25\x45\x50\x02\x00"]
|
||||
# Alias for DF_GA_MAESTRO
|
||||
APPLICATIONS["\xA0\x00\x00\x00\x04\x30\x60"] = APPLICATIONS["\xD2\x76\x00\x00\x25\x47\x41\x01\x00"]
|
||||
|
||||
## Format: "RID (binary)": ("vendor name", [optional: {more information}])
|
||||
VENDORS = {
|
||||
"\xD2\x76\x00\x01\x24": ("Free Software Foundation Europe", ),
|
||||
"\xD2\x76\x00\x00\x25": ("Bankenverlag", ),
|
||||
"\xD2\x76\x00\x00\x60": ("Wolfgang Rankl", ),
|
||||
"\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):
|
||||
|
@ -125,44 +83,15 @@ class Card:
|
|||
# Static method for when there is no object reference
|
||||
return Card._decode_df_name(value)
|
||||
|
||||
TLV_OBJECTS[TLV_utils.context_FCP] = {
|
||||
0x84: (decode_df_name, "DF name"),
|
||||
}
|
||||
TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]
|
||||
|
||||
def __init__(self, reader):
|
||||
self.reader = reader
|
||||
|
||||
self._i = 0
|
||||
self.last_apdu = None
|
||||
self.last_sw = None
|
||||
self.last_result = None
|
||||
self.sw_changed = False
|
||||
self._last_start = None
|
||||
self.last_delta = None
|
||||
|
||||
def post_merge(self):
|
||||
## Called after cards.__init__.Cardmultiplexer._merge_attributes
|
||||
self.TLV_OBJECTS[TLV_utils.context_FCP][0x84] = (self._decode_df_name, "DF name")
|
||||
self.TLV_OBJECTS[TLV_utils.context_FCI][0x84] = (self._decode_df_name, "DF name")
|
||||
|
||||
def verify_pin(self, pin_number, pin_value):
|
||||
apdu = C_APDU(self.APDU_VERIFY_PIN, P2 = pin_number,
|
||||
data = pin_value)
|
||||
result = self.send_apdu(apdu)
|
||||
return self.check_sw(result.sw)
|
||||
|
||||
def cmd_verify(self, pin_number, pin_value):
|
||||
"""Verify a PIN."""
|
||||
pin_number = int(pin_number, 0)
|
||||
pin_value = binascii.a2b_hex("".join(pin_value.split()))
|
||||
self.verify_pin(pin_number, pin_value)
|
||||
|
||||
def cmd_reset(self):
|
||||
"""Reset the card."""
|
||||
# FIXME
|
||||
raise NotImplementedException
|
||||
|
||||
def cmd_parsetlv(self, start = None, end = None):
|
||||
"Decode the TLV data in the last response, start and end are optional"
|
||||
lastlen = len(self.last_result.data)
|
||||
|
@ -189,9 +118,13 @@ class Card:
|
|||
"description": len(info) > 1 and info[1] or ""
|
||||
}
|
||||
|
||||
def cmd_reset(self):
|
||||
"""Reset the card."""
|
||||
# FIXME
|
||||
raise NotImplementedException
|
||||
|
||||
COMMANDS = {
|
||||
"reset": cmd_reset,
|
||||
"verify": cmd_verify,
|
||||
"parse_tlv": cmd_parsetlv,
|
||||
"show_applications": cmd_show_applications,
|
||||
}
|
||||
|
@ -203,11 +136,9 @@ class Card:
|
|||
print ">> " + utils.hexdump(apdu_binary, indent = 3)
|
||||
|
||||
result_binary = self.reader.transceive(apdu_binary)
|
||||
result = R_APDU(result_binary)
|
||||
result = apdu.RESPONSE_CLASS(result_binary)
|
||||
|
||||
self.last_apdu = apdu
|
||||
self.last_sw = result.sw
|
||||
self.sw_changed = True
|
||||
|
||||
if DEBUG:
|
||||
print "<< " + utils.hexdump(result_binary, indent = 3)
|
||||
|
@ -216,11 +147,11 @@ class Card:
|
|||
def _send_with_retry(self, apdu):
|
||||
result = self._real_send(apdu)
|
||||
|
||||
if self.check_sw(result.sw, PURPOSE_GET_RESPONSE):
|
||||
if self.check_sw(result.sw, self.PURPOSE_GET_RESPONSE):
|
||||
## Need to call GetResponse
|
||||
gr_apdu = C_APDU(self.APDU_GET_RESPONSE, le = result.sw2, cla=apdu.cla) # FIXME
|
||||
gr_apdu = C_APDU(self.COMMAND_GET_RESPONSE, le = result.sw2, cla=apdu.cla) # FIXME
|
||||
result = R_APDU(self._real_send(gr_apdu))
|
||||
elif self.check_sw(result.sw, PURPOSE_RETRY) and apdu.Le == 0:
|
||||
elif self.check_sw(result.sw, self.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))
|
||||
|
@ -303,29 +234,6 @@ class Card:
|
|||
return None
|
||||
match_statusword = staticmethod(match_statusword)
|
||||
|
||||
def decode_statusword(self):
|
||||
if self.last_sw is None:
|
||||
return "No command executed so far"
|
||||
else:
|
||||
retval = None
|
||||
|
||||
matched_sw = self.match_statusword(self.STATUS_WORDS.keys(), self.last_sw)
|
||||
if matched_sw is not None:
|
||||
retval = self.STATUS_WORDS.get(matched_sw)
|
||||
if isinstance(retval, str):
|
||||
retval = retval % { "SW1": ord(self.last_sw[0]),
|
||||
"SW2": ord(self.last_sw[1]) }
|
||||
|
||||
elif callable(retval):
|
||||
retval = retval( ord(self.last_sw[0]),
|
||||
ord(self.last_sw[1]) )
|
||||
|
||||
if retval is None:
|
||||
return "Unknown SW (SW %s)" % binascii.b2a_hex(self.last_sw)
|
||||
else:
|
||||
return "%s (SW %s)" % (retval, binascii.b2a_hex(self.last_sw))
|
||||
|
||||
|
||||
def get_driver_name(self):
|
||||
if len(self.DRIVER_NAME) > 1:
|
||||
names = [e for e in self.DRIVER_NAME if e != _GENERIC_NAME]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import utils
|
||||
from generic_card import *
|
||||
from iso_card import *
|
||||
|
||||
class GSM_Card(Card):
|
||||
class GSM_Card(ISO_Card):
|
||||
DRIVER_NAME = ["GSM"]
|
||||
APDU_GET_RESPONSE = C_APDU("\xa0\xC0\x00\x00")
|
||||
COMMAND_GET_RESPONSE = C_APDU("\xa0\xC0\x00\x00")
|
||||
|
||||
STATUS_MAP = {
|
||||
PURPOSE_GET_RESPONSE: ("9F??", )
|
||||
Card.PURPOSE_GET_RESPONSE: ("9F??", )
|
||||
}
|
||||
|
||||
ATRS = [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys;sys.path.append(".."); sys.path.append(".")
|
||||
import TLV_utils
|
||||
from generic_card import *
|
||||
from iso_card import *
|
||||
from generic_application import Application
|
||||
import building_blocks
|
||||
|
||||
|
@ -156,7 +156,7 @@ class iso_df(iso_node):
|
|||
if node not in self._children:
|
||||
self._children.append(node)
|
||||
|
||||
class ISO_7816_4_Card(building_blocks.Card_with_read_binary,Card):
|
||||
class ISO_7816_4_Card(building_blocks.Card_with_read_binary,ISO_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)
|
||||
|
@ -284,18 +284,18 @@ class ISO_7816_4_Card(building_blocks.Card_with_read_binary,Card):
|
|||
aid = self.resolve_symbolic_aid(application)
|
||||
Application.load_applications(self, aid)
|
||||
|
||||
ATRS = list(Card.ATRS)
|
||||
ATRS = list(ISO_Card.ATRS)
|
||||
ATRS.extend( [
|
||||
(".*", None), ## For now we accept any card
|
||||
] )
|
||||
|
||||
STOP_ATRS = list(Card.STOP_ATRS)
|
||||
STOP_ATRS = list(ISO_Card.STOP_ATRS)
|
||||
STOP_ATRS.extend( [
|
||||
("3b8f8001804f0ca000000306......00000000..", None), # Contactless storage cards (PC/SC spec part 3 section 3.1.3.2.3
|
||||
("3b8180018080", None), # Mifare DESfire (special case of contactless smartcard, ibid.)
|
||||
] )
|
||||
|
||||
COMMANDS = dict(Card.COMMANDS)
|
||||
COMMANDS = dict(ISO_Card.COMMANDS)
|
||||
COMMANDS.update(building_blocks.Card_with_read_binary.COMMANDS)
|
||||
COMMANDS.update( {
|
||||
"select_application": cmd_selectapplication,
|
||||
|
@ -307,7 +307,7 @@ class ISO_7816_4_Card(building_blocks.Card_with_read_binary,Card):
|
|||
"next_record": cmd_next_record,
|
||||
} )
|
||||
|
||||
STATUS_WORDS = dict(Card.STATUS_WORDS)
|
||||
STATUS_WORDS = dict(ISO_Card.STATUS_WORDS)
|
||||
STATUS_WORDS.update( {
|
||||
"62??": "Warning, State of non-volatile memory unchanged",
|
||||
"63??": "Warning, State of non-volatile memory changed",
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
import smartcard
|
||||
import TLV_utils, crypto_utils, utils, binascii, fnmatch, re, time
|
||||
from generic_card import Card
|
||||
from utils import C_APDU, R_APDU
|
||||
|
||||
class ISO_Card(Card):
|
||||
DRIVER_NAME = ["ISO"]
|
||||
COMMAND_GET_RESPONSE = C_APDU(ins=0xc0)
|
||||
|
||||
APDU_VERIFY_PIN = C_APDU(ins=0x20)
|
||||
|
||||
## Map for check_sw()
|
||||
STATUS_MAP = {
|
||||
Card.PURPOSE_SUCCESS: ("\x90\x00", ),
|
||||
Card.PURPOSE_GET_RESPONSE: ("61??", ), ## If this is received then GET RESPONSE should be called with SW2
|
||||
Card.PURPOSE_SM_OK: ("\x90\x00",),
|
||||
Card.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
|
||||
}
|
||||
|
||||
ATRS = list(Card.ATRS)
|
||||
STOP_ATRS = list(Card.STOP_ATRS)
|
||||
|
||||
## Note: a key in this dictionary may either be a one- or two-byte string containing
|
||||
## a binary status word, or a two or four-byte string containing a hexadecimal
|
||||
## status word, possibly with ? characters marking variable nibbles.
|
||||
## Hexadecimal characters MUST be in uppercase. The values that two- or four-byte
|
||||
## strings map to may be either format strings, that can make use of the
|
||||
## keyword substitutions for SW1 and SW2 or a callable accepting two arguments
|
||||
## (SW1, SW2) that returns a string.
|
||||
STATUS_WORDS = {
|
||||
'\x90\x00': "Normal execution",
|
||||
'61??': "%(SW2)i (0x%(SW2)02x) bytes of response data can be retrieved with GetResponse.",
|
||||
'6C??': "Bad value for LE, 0x%(SW2)02x is the correct value.",
|
||||
'63C?': lambda SW1,SW2: "The counter has reached the value '%i'" % (SW2%16)
|
||||
}
|
||||
## For the format of this dictionary of dictionaries see TLV_utils.tags
|
||||
TLV_OBJECTS = dict(Card.TLV_OBJECTS)
|
||||
DEFAULT_CONTEXT = None
|
||||
|
||||
## Format: "AID (binary)": ("name", [optional: description, {more information}])
|
||||
APPLICATIONS = {
|
||||
"\xa0\x00\x00\x01\x67\x45\x53\x49\x47\x4e": ("DF.ESIGN", ),
|
||||
"\xa0\x00\x00\x00\x63\x50\x4b\x43\x53\x2d\x31\x35": ("DF_PKCS15", ),
|
||||
"\xD2\x76\x00\x01\x24\x01": ("DF_OpenPGP", "OpenPGP card", {"significant_length": 6} ),
|
||||
"\xa0\x00\x00\x02\x47\x10\x01": ("DF_LDS", "Machine Readable Travel Document", {"alias": ("mrtd",)}),
|
||||
## The following are from 0341a.pdf: BSI-DSZ-CC-0341-2006
|
||||
"\xD2\x76\x00\x00\x66\x01": ("DF_SIG", "Signature application", {"fid": "\xAB\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x5A\x41\x02\x00": ("ZA_MF_NEU", "Zusatzanwendungen", {"fid": "\xA7\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x45\x43\x02\x00": ("DF_EC_CASH_NEU", "ec-Cash", {"fid": "\xA1\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x45\x50\x02\x00": ("DF_BOERSE_NEU", "Geldkarte", {"fid": "\xA2\x00", "alias": ("geldkarte",)}),
|
||||
"\xD2\x76\x00\x00\x25\x47\x41\x01\x00": ("DF_GA_MAESTRO", "GA-Maestro", {"fid": "\xAC\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x54\x44\x01\x00": ("DF_TAN", "TAN-Anwendung", {"fid": "\xAC\x02"}),
|
||||
"\xD2\x76\x00\x00\x25\x4D\x01\x02\x00": ("DF_MARKTPLATZ_NEU", "Marktplatz", {"fid": "\xB0\x01"}),
|
||||
"\xD2\x76\x00\x00\x25\x46\x53\x02\x00": ("DF_FAHRSCHEIN_NEU", "Fahrschein", {"fid": "\xB0\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x48\x42\x02\x00": ("DF_BANKING_20" , "HBCI", {"fid": "\xA6\x00"}),
|
||||
"\xD2\x76\x00\x00\x25\x4E\x50\x01\x00": ("DF_NOTEPAD", "Notepad", {"fid": "\xA6\x10"}),
|
||||
|
||||
"\xd2\x76\x00\x00\x85\x01\x00": ("NFC_TYPE_4", "NFC NDEF Application on tag type 4", {"alias": ("nfc",)}, ),
|
||||
|
||||
# From TR-03110_v201_pdf.pdf
|
||||
"\xE8\x07\x04\x00\x7f\x00\x07\x03\x02": ("DF_eID", "eID application"),
|
||||
|
||||
"\xd2\x76\x00\x00\x25\x4b\x41\x4e\x4d\x30\x31\x00": ("VRS_TICKET", "VRS Ticket", {"fid": "\xad\x00", "alias": ("vrs",)}, ),
|
||||
"\xd2\x76\x00\x01\x35\x4b\x41\x4e\x4d\x30\x31\x00": ("VRS_TICKET", "VRS Ticket", {"fid": "\xad\x00",}, ),
|
||||
}
|
||||
# Alias for DF_BOERSE_NEU
|
||||
APPLICATIONS["\xA0\x00\x00\x00\x59\x50\x41\x43\x45\x01\x00"] = APPLICATIONS["\xD2\x76\x00\x00\x25\x45\x50\x02\x00"]
|
||||
# Alias for DF_GA_MAESTRO
|
||||
APPLICATIONS["\xA0\x00\x00\x00\x04\x30\x60"] = APPLICATIONS["\xD2\x76\x00\x00\x25\x47\x41\x01\x00"]
|
||||
|
||||
## Format: "RID (binary)": ("vendor name", [optional: {more information}])
|
||||
VENDORS = {
|
||||
"\xD2\x76\x00\x01\x24": ("Free Software Foundation Europe", ),
|
||||
"\xD2\x76\x00\x00\x25": ("Bankenverlag", ),
|
||||
"\xD2\x76\x00\x00\x60": ("Wolfgang Rankl", ),
|
||||
"\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", ),
|
||||
}
|
||||
|
||||
TLV_OBJECTS[TLV_utils.context_FCP] = {
|
||||
0x84: (Card.decode_df_name, "DF name"),
|
||||
}
|
||||
TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]
|
||||
|
||||
def __init__(self, reader):
|
||||
Card.__init__(self, reader)
|
||||
self.last_sw = None
|
||||
self.sw_changed = False
|
||||
|
||||
def post_merge(self):
|
||||
## Called after cards.__init__.Cardmultiplexer._merge_attributes
|
||||
self.TLV_OBJECTS[TLV_utils.context_FCP][0x84] = (self._decode_df_name, "DF name")
|
||||
self.TLV_OBJECTS[TLV_utils.context_FCI][0x84] = (self._decode_df_name, "DF name")
|
||||
|
||||
def decode_statusword(self):
|
||||
if self.last_sw is None:
|
||||
return "No command executed so far"
|
||||
else:
|
||||
retval = None
|
||||
|
||||
matched_sw = self.match_statusword(self.STATUS_WORDS.keys(), self.last_sw)
|
||||
if matched_sw is not None:
|
||||
retval = self.STATUS_WORDS.get(matched_sw)
|
||||
if isinstance(retval, str):
|
||||
retval = retval % { "SW1": ord(self.last_sw[0]),
|
||||
"SW2": ord(self.last_sw[1]) }
|
||||
|
||||
elif callable(retval):
|
||||
retval = retval( ord(self.last_sw[0]),
|
||||
ord(self.last_sw[1]) )
|
||||
|
||||
if retval is None:
|
||||
return "Unknown SW (SW %s)" % binascii.b2a_hex(self.last_sw)
|
||||
else:
|
||||
return "%s (SW %s)" % (retval, binascii.b2a_hex(self.last_sw))
|
||||
|
||||
def _real_send(self, apdu):
|
||||
result = Card._real_send(self, apdu)
|
||||
|
||||
self.last_sw = result.sw
|
||||
self.sw_changed = True
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
def verify_pin(self, pin_number, pin_value):
|
||||
apdu = C_APDU(self.APDU_VERIFY_PIN, P2 = pin_number,
|
||||
data = pin_value)
|
||||
result = self.send_apdu(apdu)
|
||||
return self.check_sw(result.sw)
|
||||
|
||||
def cmd_verify(self, pin_number, pin_value):
|
||||
"""Verify a PIN."""
|
||||
pin_number = int(pin_number, 0)
|
||||
pin_value = binascii.a2b_hex("".join(pin_value.split()))
|
||||
self.verify_pin(pin_number, pin_value)
|
||||
|
||||
COMMANDS = {
|
||||
"verify": cmd_verify,
|
||||
}
|
||||
|
|
@ -1,13 +1,10 @@
|
|||
import utils, binascii
|
||||
from generic_card import *
|
||||
from iso_card import *
|
||||
from utils import C_APDU
|
||||
|
||||
class Java_Card(Card):
|
||||
class Java_Card(ISO_Card):
|
||||
DRIVER_NAME = ["Generic Java"]
|
||||
APPLICATIONS = {
|
||||
"\xa0\x00\x00\x00\x01\x01": ("muscle", "MUSCLE applet")
|
||||
}
|
||||
|
||||
def __init__(self, card = None):
|
||||
Card.__init__(self, card = card)
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import struct, binascii, os, datetime, sys
|
|||
from hashlib import sha1
|
||||
from utils import hexdump, C_APDU
|
||||
from tcos_card import SE_Config, TCOS_Security_Environment
|
||||
from generic_card import Card
|
||||
from iso_7816_4_card import ISO_7816_4_Card
|
||||
from iso_7816_4_card import ISO_7816_4_Card, ISO_Card, Card
|
||||
import crypto_utils, tcos_card, TLV_utils, generic_card
|
||||
from TLV_utils import identifier
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import utils, binascii
|
||||
from generic_card import *
|
||||
from iso_card import *
|
||||
|
||||
class PN532_Virtual_Card(Card):
|
||||
class PN532_Virtual_Card(ISO_Card):
|
||||
# This is a virtual card that is enabled for the ACS ACR reader that
|
||||
# contains a PN532 module
|
||||
DRIVER_NAME = ["PN532"]
|
||||
|
||||
STATUS_WORDS = dict(Card.STATUS_WORDS)
|
||||
COMMANDS = dict(Card.COMMANDS)
|
||||
STATUS_WORDS = dict(ISO_Card.STATUS_WORDS)
|
||||
COMMANDS = dict(ISO_Card.COMMANDS)
|
||||
|
||||
APDU_TRANSCEIVE_PN532 = C_APDU(cla=0xff, ins=0, p1=0, p2=0)
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ from iso_7816_4_card import *
|
|||
class Postcard_Card(ISO_7816_4_Card):
|
||||
DRIVER_NAME = ["Postcard"]
|
||||
CLA = 0xbc
|
||||
APDU_GET_RESPONSE = C_APDU(cla=CLA,ins=0xc0)
|
||||
COMMAND_GET_RESPONSE = C_APDU(cla=CLA,ins=0xc0)
|
||||
|
||||
APDU_SELECT_FILE = C_APDU(cla=CLA,ins=0xa4)
|
||||
APDU_READ_BINARY = C_APDU(cla=CLA,ins=0xb0,le=0x80)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import utils
|
||||
from generic_card import *
|
||||
from iso_card import *
|
||||
import building_blocks
|
||||
|
||||
class RFID_Card(Card):
|
||||
class RFID_Card(ISO_Card):
|
||||
DRIVER_NAME = ["RFID"]
|
||||
APDU_GET_UID = utils.C_APDU(CLA=0xff, INS=0xCA, p1=0, p2=0, Le=0)
|
||||
|
||||
|
@ -32,7 +32,7 @@ class RFID_Card(Card):
|
|||
"get_uid": cmd_get_uid,
|
||||
}
|
||||
|
||||
STATUS_WORDS = dict(Card.STATUS_WORDS)
|
||||
STATUS_WORDS = dict(ISO_Card.STATUS_WORDS)
|
||||
STATUS_WORDS.update( {
|
||||
"\x62\x82": "End of file (or UID) reached before Le bytes",
|
||||
"\x67\x00": "Wrong Length",
|
||||
|
|
|
@ -12,7 +12,7 @@ class Starcos_Card(ISO_7816_4_Card):
|
|||
else:
|
||||
return self.select_file(0x00, 0x0C, fid)
|
||||
|
||||
ATRS = list(Card.ATRS)
|
||||
ATRS = list(ISO_Card.ATRS)
|
||||
ATRS.extend( [
|
||||
("3bb794008131fe6553504b32339000d1", None),
|
||||
] )
|
||||
|
|
59
utils.py
59
utils.py
|
@ -129,21 +129,17 @@ def _make_byte_property(prop):
|
|||
lambda self: delattr(self, "_"+prop),
|
||||
"The %s attribute of the APDU" % prop)
|
||||
|
||||
class APDU(object):
|
||||
"Base class for an APDU"
|
||||
|
||||
class Transmission_Frame(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Creates a new APDU instance. Can be given positional parameters which
|
||||
"""Creates a new frame instance. Can be given positional parameters which
|
||||
must be sequences of either strings (or strings themselves) or integers
|
||||
specifying byte values that will be concatenated in order. Alternatively
|
||||
you may give exactly one positional argument that is an APDU instance.
|
||||
you may give exactly one positional argument that is an frame instance.
|
||||
After all the positional arguments have been concatenated they must
|
||||
form a valid APDU!
|
||||
form a valid frame!
|
||||
|
||||
The keyword arguments can then be used to override those values.
|
||||
Keywords recognized are:
|
||||
C_APDU: cla, ins, p1, p2, lc, le, data
|
||||
R_APDU: sw, sw1, sw2, data
|
||||
Keywords recognized are class-specific, but always include data
|
||||
"""
|
||||
|
||||
initbuff = list()
|
||||
|
@ -168,7 +164,7 @@ class APDU(object):
|
|||
if t == str:
|
||||
initbuff[index] = ord(value)
|
||||
elif t != int:
|
||||
raise TypeError, "APDU must consist of ints or one-byte strings, not %s (index %s)" % (t, index)
|
||||
raise TypeError, "Frame must consist of ints or one-byte strings, not %s (index %s)" % (t, index)
|
||||
|
||||
self.parse( initbuff )
|
||||
|
||||
|
@ -190,7 +186,7 @@ class APDU(object):
|
|||
del self._data; self.data = ""
|
||||
|
||||
data = property(_getdata, _setdata, None,
|
||||
"The data contents of this APDU")
|
||||
"The data contents of this frame")
|
||||
|
||||
def _setbyte(self, name, value):
|
||||
#print "setbyte(%r, %r)" % (name, value)
|
||||
|
@ -229,8 +225,24 @@ class APDU(object):
|
|||
|
||||
return "%s(%s)" % (self.__class__.__name__, ", ".join(parts))
|
||||
|
||||
class C_APDU(APDU):
|
||||
"Class for a command APDU"
|
||||
class Command_Frame(Transmission_Frame):
|
||||
pass
|
||||
|
||||
class Response_Frame(Transmission_Frame):
|
||||
pass
|
||||
|
||||
Transmission_Frame.COMMAND_CLASS = Command_Frame
|
||||
Transmission_Frame.RESPONSE_CLASS = Response_Frame
|
||||
|
||||
class APDU(Transmission_Frame):
|
||||
"Base class for an APDU"
|
||||
|
||||
class C_APDU(Command_Frame,APDU):
|
||||
"""Class for a command APDU
|
||||
|
||||
Recognized keywords for __init__ are:
|
||||
cla, ins, p1, p2, lc, le, data
|
||||
"""
|
||||
|
||||
def parse(self, apdu):
|
||||
"Parse a full command APDU and assign the values to our object, overwriting whatever there was."
|
||||
|
@ -424,9 +436,12 @@ class C_APDU(APDU):
|
|||
apdu = C_APDU(apdu_head + apdu_tail, marks = marks)
|
||||
return apdu
|
||||
|
||||
|
||||
class R_APDU(APDU):
|
||||
"Class for a response APDU"
|
||||
class R_APDU(Response_Frame,APDU):
|
||||
"""Class for a response APDU
|
||||
|
||||
Recognized keywords for __init__ are:
|
||||
sw, sw1, sw2, data
|
||||
"""
|
||||
|
||||
def _getsw(self): return chr(self.SW1) + chr(self.SW2)
|
||||
def _setsw(self, value):
|
||||
|
@ -458,6 +473,9 @@ class R_APDU(APDU):
|
|||
"Return this APDU as a binary string"
|
||||
return self.data + self.sw
|
||||
|
||||
APDU.COMMAND_CLASS = C_APDU
|
||||
APDU.RESPONSE_CLASS = R_APDU
|
||||
|
||||
class Raw_APDU(APDU):
|
||||
"""Raw APDU that doesn't do any parsing"""
|
||||
|
||||
|
@ -472,7 +490,7 @@ class Raw_APDU(APDU):
|
|||
def _format_fields(self):
|
||||
return ""
|
||||
|
||||
class PN532_Frame(APDU):
|
||||
class PN532_Frame(Transmission_Frame):
|
||||
"""This is not really an ISO 7816 APDU, but close enough to use the same
|
||||
class infrastructure."""
|
||||
|
||||
|
@ -540,12 +558,15 @@ class PN532_Frame(APDU):
|
|||
def render(self):
|
||||
return chr(self.cmd) + chr(self.dir) + self.data
|
||||
|
||||
class PN532_Command(PN532_Frame):
|
||||
class PN532_Command(Command_Frame, PN532_Frame):
|
||||
MATCH_BY_dir = _DEFAULT_DIR = 0xd4
|
||||
|
||||
class PN532_Response(PN532_Frame):
|
||||
class PN532_Response(Response_Frame, PN532_Frame):
|
||||
MATCH_BY_dir = _DEFAULT_DIR = 0xd5
|
||||
|
||||
PN532_Frame.COMMAND_CLASS = PN532_Command
|
||||
PN532_Frame.RESPONSE_CLASS = PN532_Response
|
||||
|
||||
class PN532_Target(object):
|
||||
TYPE_ISO14443A = "ISO 14443-A"
|
||||
TYPE_ISO14443B = "ISO 14443-B"
|
||||
|
|
Loading…
Reference in New Issue