Merge branch 'desfire-native'

This commit is contained in:
Henryk Plötz 2010-10-15 17:11:39 +02:00
commit 367b41a061
11 changed files with 232 additions and 160 deletions

View File

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

View File

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

View File

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

145
cards/iso_card.py Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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