cyberflex-shell/cards/tcos_card.py

800 lines
31 KiB
Python

import utils, TLV_utils, crypto_utils, traceback
from iso_7816_4_card import *
import building_blocks, generic_card
MODE_ECB = 0
MODE_CBC = 1
ALGO_IDEA = 0x1
ALGO_DES = 0x2
ALGO_DES3 = 0x3
SE_APDU = 1
SE_RAPDU = 2
SE_PSO = 3
TEMPLATE_CCT = 0xB4 # Template for Cryptographic Checksum
TEMPLATE_CT = 0xB8 # Template for Confidentiality
PI_ISO = 1 # Padding indicator for ISO padding (\x80\x00...)
class SE_Config:
def __init__(self, config = None):
self.algorithm = None
self.mode = MODE_ECB
self.keyref = 0
self.keytype = 0
self.iv = "\x00" * 8
self.context = None
self.operation = None
if config is not None:
self.parse(config)
def parse(self, config):
structure = TLV_utils.unpack(config)
for data in structure:
tag, length, value = data
if tag == 0x80:
self.mode = ord(value[0]) & 1
algorithm = (ord(value[0]) >> 2) & 0x7
self.algorithm = algorithm
elif tag in (0x83, 0x84):
self.keyref = ord(value)
self.keytype = tag
elif tag == 0x85:
self.iv = "\x00" * 8
elif tag == 0x87:
self.iv = value
elif tag == 0x88:
self.iv = None ## FIXME
else:
print "Warning: Unknown MSE parameters: tag 0x%02x, length 0x%02x, value: %s" % (tag, length, utils.hexdump(value, short=True))
class TCOS_Security_Environment(object):
MARK_ENCRYPT = "["
def __init__(self, card):
self.keys = {}
self.card = card
self.last_c_apdu = None
self.last_r_apdu = None
self.config = {}
def have_config(self, context, operation):
return self.config.has_key( (context, operation) )
def get_config(self, context, operation):
if not self.have_config(context, operation):
self.set_config( context, operation, SE_Config() )
return self.config[ context, operation ]
def set_config(self, context, operation, config):
self.config[ context, operation ] = config
config.context = context
config.operation = operation
def before_send(self, apdu):
self.last_c_apdu = apdu
if apdu.cla & 0x0c in (0x08, 0x0c):
apdu = self.process_apdu(apdu)
return apdu
def after_send(self, result):
self.last_r_apdu = result
if self.card.check_sw(result.sw, self.card.PURPOSE_SM_OK):
if (self.last_c_apdu.cla & 0xf0) == 0x00:
if self.last_c_apdu.ins == 0x22:
self.parse_mse(self.last_c_apdu)
if (self.last_c_apdu.cla & 0x0c) in (0x08, 0x0c):
result = self.process_rapdu(result)
return result
def process_apdu(self, apdu):
if apdu.cla & 0x0c in (0x0c, 0x08):
tlv_data = TLV_utils.unpack(apdu.data, with_marks = apdu.marks, include_filler=True)
tlv_data = self.encrypt_command(tlv_data)
tlv_data = self.authenticate_command(apdu, tlv_data)
data = TLV_utils.pack(tlv_data, recalculate_length = True)
new_apdu = C_APDU(apdu, data = data)
return new_apdu
else:
return apdu
def process_rapdu(self, rapdu):
result = rapdu
if self.last_c_apdu.cla & 0x0c in (0x0c, 0x08):
tlv_c_data = TLV_utils.unpack(self.last_c_apdu.data)
must_authenticate = False
must_decrypt = False
for data in tlv_c_data:
if data[0] & ~0x01 == 0xBA:
for response_template in data[2]:
if response_template[0] == 0x8E:
must_authenticate = True
if response_template[0] & ~0x01 in (0x84, 0x86):
must_decrypt = True
if must_authenticate or must_decrypt:
tlv_data = TLV_utils.unpack(rapdu.data, include_filler=True)
try:
if must_authenticate:
tlv_data = self.authenticate_response(tlv_data)
if must_decrypt:
tlv_data = self.decrypt_response(tlv_data)
#data = TLV_utils.pack(tlv_data, recalculate_length = True)
data, sw = self.deformat_response(tlv_data, rapdu.sw)
new_apdu = R_APDU(rapdu, data = data, sw = sw)
result = new_apdu
except ValueError:
print "Warning: Can't authenticate/decrypt response due to exception being raised"
traceback.print_exc(limit=2)
return result
def deformat_response(self, tlv_data, sw):
WHITELIST = (0x84, 0x86, 0x98)
result = []
is_ok = True
for data in tlv_data:
t = data[0] & ~0x01
if t not in WHITELIST and t in range(0x80, 0xBF+1):
is_ok = False # Unrecognized SM field present
if is_ok:
for data in tlv_data:
t = data[0] & ~0x01
value = data[2]
if t in WHITELIST:
if t == 0x86:
result.append( value[1:] )
elif t == 0x98:
if sw != value:
print "Warning: SW from SM not equal SW from RAPDU"
sw = value
else:
result.append( value )
else:
result.append( TLV_utils.pack( (data,), recalculate_length = True) )
else:
result.append( TLV_utils.pack( tlv_data, recalculate_length = True) )
return "".join(result), sw
def encrypt_command(self, tlv_data):
config = self.get_config(SE_APDU, TEMPLATE_CT)
if config.algorithm is None: ## FIXME: Find out the correct way to determine this
return tlv_data
result = []
for data in tlv_data:
tag, length, value, marks = data
if self.MARK_ENCRYPT in marks and tag not in (0xff, 0x00):
t = tag & ~(0x01)
if t == 0x84:
value_ = self.pad(value)
if generic_card.DEBUG:
print "| Tag 0x%02x, length 0x%02x, encrypting (with ISO padding): " % (tag, length)
print "|| " + "\n|| ".join( utils.hexdump( value_ ).splitlines() )
value = crypto_utils.cipher( True,
self.get_cipherspec(config),
self.get_key(config),
value_,
self.get_iv(config) )
if generic_card.DEBUG:
print "| Encrypted result of length 0x%02x:" % len(value)
print "|| " + "\n|| ".join( utils.hexdump(value).splitlines() )
print
elif t == 0x86:
pi = value[0]
value_ = self.pad(value[1:], ord(pi))
if generic_card.DEBUG:
print "| Tag 0x%02x, length 0x%02x, encrypting (with padding type %x): " % (tag, length, ord(pi))
print "|| " + "\n|| ".join( utils.hexdump( value_ ).splitlines() )
value = pi + crypto_utils.cipher( True,
self.get_cipherspec(config),
self.get_key(config),
value_,
self.get_iv(config) )
if generic_card.DEBUG:
print "| Encrypted result of length 0x%02x:" % len(value)
print "|| " + "\n|| ".join( utils.hexdump(value).splitlines() )
print
result.append( (tag, length, value) )
else: # Ignore
result.append(data[:3])
return result
def decrypt_response(self, tlv_data):
config = self.get_config(SE_RAPDU, TEMPLATE_CT)
if config.algorithm is None: ## FIXME: Find out the correct way to determine this
return tlv_data
result = []
for data in tlv_data:
tag, length, value = data[:3]
marks = len(data) > 3 and data[3] or ()
t = tag & ~(0x01)
if t == 0x84:
if generic_card.DEBUG:
print
print "| Tag 0x%02x, length 0x%02x, encrypted (with ISO padding): " % (tag, length)
print "|| " + "\n|| ".join( utils.hexdump( value ).splitlines() )
value_ = crypto_utils.cipher( False,
self.get_cipherspec(config),
self.get_key(config),
value,
self.get_iv(config) )
if generic_card.DEBUG:
print "| Decrypted result of length 0x%02x:" % len(value_)
print "|| " + "\n|| ".join( utils.hexdump(value_).splitlines() )
value = self.unpad(value_)
if False:
print "| Depadded result of length 0x%02x:" % len(value)
print "|| " + "\n|| ".join( utils.hexdump(value).splitlines() )
marks = marks + (self.MARK_ENCRYPT,)
elif t == 0x86:
pi = value[0]
if generic_card.DEBUG:
print
print "| Tag 0x%02x, length 0x%02x, decrypting (with padding type %x): " % (tag, length, ord(pi))
print "|| " + "\n|| ".join( utils.hexdump( value[1:] ).splitlines() )
value_ = crypto_utils.cipher( False,
self.get_cipherspec(config),
self.get_key(config),
value[1:],
self.get_iv(config) )
if generic_card.DEBUG:
print "| Decrypted result of length 0x%02x:" % len(value_)
print "|| " + "\n|| ".join( utils.hexdump(value_).splitlines() )
value = self.unpad(value_, ord(pi))
if False:
print "| Depadded result of length 0x%02x:" % len(value)
print "|| " + "\n|| ".join( utils.hexdump(value).splitlines() )
value = pi + value
marks = marks + (self.MARK_ENCRYPT,)
result.append( (tag, length, value, marks) )
return result
def calculate_cct(self, config, tlv_data, startblock = "", print_buffer=True):
"""Calculate the Cryptographic Checksum for some TLV data.
tlv_data MUST be of the format generated by the include_filler=True parameter to unpack."""
if print_buffer:
print "| Calculating cryptographic checksum:"
def do_block(buffer, block):
block_ = self.pad("".join(block), pi = PI_ISO)
offset = sum( [len(b) for b in buffer] )
buffer.append(block_)
del block[:]
if print_buffer:
print "|| " + "\n|| ".join( utils.hexdump( block_, offset = offset ).splitlines() )
buffer = []
if startblock != "":
do_block(buffer, [startblock])
block = []
for data in tlv_data:
tag, length, value = data[:3]
if (tag & 0x01 == 0x01 or tag not in range(0x80, 0xbf+1)) and tag not in (0xff, 0x00):
value_ = TLV_utils.pack( (data, ), recalculate_length=True )
block.append( value_ )
elif tag in (0xff, 0x00) and len(block) > 0:
block.append( chr(tag) )
else:
if len(block) > 0:
do_block(buffer, block)
if len(block) > 0:
do_block(buffer, block)
cct = self._mac(config, "".join(buffer))
if print_buffer:
print "| Result (Tag 0x8e, length: 0x%02x):" % len(cct)
print "|| " + "\n|| ".join( utils.hexdump( cct ).splitlines() )
return cct
def _mac(self, config, data):
return crypto_utils.cipher( True,
self.get_cipherspec(config),
self.get_key(config),
data,
self.get_iv(config) )[-8:]
def authenticate_command(self, apdu, tlv_data):
config = self.get_config(SE_APDU, TEMPLATE_CCT)
if config.algorithm is None: ## FIXME: Find out the correct way to determine this
return tlv_data
result = []
for data in tlv_data:
if data[0] == 0x8e and data[1] == 0:
startblock = ""
if apdu.cla & 0x0c == 0x0c:
startblock = apdu.render()[:4]
cct = self.calculate_cct(config, tlv_data, startblock, print_buffer=generic_card.DEBUG)
if generic_card.DEBUG:
print
data = tuple( (0x8e, len(cct), cct) + data[3:] )
result.append(data)
return result
def authenticate_response(self, tlv_data):
config = self.get_config(SE_RAPDU, TEMPLATE_CCT)
if config.algorithm is None: ## FIXME: Find out the correct way to determine this
return tlv_data
if generic_card.DEBUG:
print
cct_claimed = None
result = []
for data in tlv_data:
if data[0] == 0x8E:
cct_claimed = data[2]
else:
result.append( data )
if cct_claimed is None:
if generic_card.DEBUG:
print "| CRYPTOGRAPHIC CHECKSUM VERIFICATION ERROR"
print "| No cryptographic checksum was included in the response"
return tlv_data
else:
cct = self.calculate_cct(config, tlv_data, print_buffer=generic_card.DEBUG)
if len(cct_claimed) >= 4 and cct.startswith(cct_claimed):
if generic_card.DEBUG:
print "| Cryptographic checksum verifies OK"
return result
else:
if generic_card.DEBUG:
print "| CRYPTOGRAPHIC CHECKSUM VERIFICATION ERROR"
print "| Is:"
print "|| " + "\n|| ".join( utils.hexdump( cct_claimed ).splitlines() )
print "| Should be:"
print "|| " + "\n|| ".join( utils.hexdump( cct ).splitlines() )
return tlv_data
def get_cipherspec(self, config):
g = globals()
spec = ""
for name in g.keys():
if name.startswith("ALGO_") and config.algorithm == g[name]:
spec = name.split("_",1)[1].lower()
if spec == "":
raise ValueError, "Unknown algorithm %s" % config.algorithm
for name in g.keys():
if name.startswith("MODE_") and config.mode == g[name]:
spec = spec + "-" + name.split("_",1)[1].lower()
if "-" not in spec:
raise ValueError, "Unknown mode %s" % config.mode
return spec
def get_key(self, config):
# FIXME stub for more intelligent key handling (e.g. asymmetric, session)
return self.keys[config.keyref]
def get_iv(self, config):
# FIXME stub for more intelligent iv handling (e.g. last random)
return config.iv
def pad(self, data, pi = 1):
if pi == 1:
topad = 8 - len(data) % 8
return data + "\x80" + ("\x00" * (topad-1))
if pi == 2:
return data
else:
raise ValueError, "Unknown padding indicator %s" % pi
def unpad(self, data, pi = 1):
if pi == 1:
pos = len(data)-1
while ord(data[pos]) != 0x80:
if ord(data[pos]) != 0x00:
raise ValueError, "Padding error"
pos = pos - 1
return data[:pos]
elif pi == 2:
return data
else:
raise ValueError, "Unknown padding indicator %s" % pi
def parse_mse(self, apdu):
assert apdu.p1 & 0x0f == 1
operation = apdu.p2
if apdu.p1 & 0x10 == 0x10:
self.set_config( SE_APDU, operation, SE_Config(apdu.data) )
if apdu.p1 & 0x20 == 0x20:
self.set_config( SE_RAPDU, operation, SE_Config(apdu.data) )
if apdu.p1 & 0xc0 == 0xc0:
self.set_config( SE_PSO, operation, SE_Config(apdu.data) )
def set_key(self, keyref, keyvalue):
self.keys[keyref] = keyvalue
class TCOS_Card(ISO_7816_4_Card,building_blocks.Card_with_80_aa):
DRIVER_NAME = ["TCOS 2.0"]
APDU_DELETE_FILE = C_APDU(cla=0x80,ins=0xe4)
SELECT_P2 = 0x04
ATRS = [
("3bba96008131865d0064........31809000..", None),
]
file_status_descriptions = (
(0xF9, 0x01, None, "Not invalidated"),
(0xF9, 0x00, None, "Invalidated"),
(0xFC, 0x04, None, "Not permanent"),
(0xFC, 0x00, None, "Permanent"),
(0xF2, 0x00, "RFU", None),
)
iftd_byte_1_descriptions = (
(0x80, 0x00, None, "Data file"),
(0xFC, 0x00, None, "RFU"),
(0x83, 0x00, None, " - general data file"),
(0x83, 0x01, None, " - system file EF_ATR"),
(0x83, 0x02, None, " - system file EF_GDO"),
(0x83, 0x03, None, " - system file EF_SIGLimit"),
(0x80, 0x80, None, "Secret file"),
(0xC0, 0x80, None, " - Password file"),
(0xFF, 0x80, None, "RFU"),
(0xC0, 0xC0, None, " - Key file"),
(0xC8, 0xC8, None, " - signature"),
(0xC4, 0xC4, None, " - encryption"),
(0xC2, 0xC2, None, " - mac"),
(0xC1, 0xC1, None, " - authenticate"),
(0xCF, 0xC0, None, "RFU"),
)
iftd_byte_3_descriptions = (
(0x10, 0x00, None, "Symmetric algorithm"),
(0x1C, 0x00, None, " - RFU"),
(0x1C, 0x04, None, " - IDEA"),
(0x1C, 0x08, None, " - DES"),
(0x1C, 0x0C, None, " - DES3"),
(0x10, 0x10, None, "Asymmetric algorithm"),
(0x9C, 0x10, None, " - RSA, Public Key"),
(0x9C, 0x90, None, " - RSA, Private Key"),
(0x63, 0x00, "RFU", None),
)
@classmethod
def decode_file_descriptor_extension(cls, value):
result = [" "+utils.hexdump(value, short=True)]
if len(value) >= 1:
result.append("File status: %s" % utils.hexdump(value[0], short=True))
result.append("\t" + "\n\t".join(
utils.parse_binary(
ord(value[0]), cls.file_status_descriptions, True
) )
)
if len(value) >= 2:
is_secret = (ord(value[1]) & 0x80 == 0x80)
is_key = (ord(value[1]) & 0xC0 == 0xC0)
if is_key:
iftd = value[1:4]
elif is_secret:
iftd = value[1:3]
else:
iftd = value[1:2]
result.append("Internal File Type Descriptor: %s" % utils.hexdump(iftd, short=True))
if len(iftd) >= 1:
result.append("\tFile Type: %s" % utils.hexdump(iftd[0], short=True))
result.append("\t\t" + "\n\t\t".join(
utils.parse_binary(
ord(iftd[0]), cls.iftd_byte_1_descriptions, True
) )
)
if len(iftd) >= 2:
result.append("\tNumber of secret: %i (0x%x)" % ((ord(iftd[1])&0x1F,)*2) )
if len(iftd) >= 3:
result.append("\tCryptographic algorithm: %s" % utils.hexdump(iftd[2], short=True))
result.append("\t\t" + "\n\t\t".join(
utils.parse_binary(
ord(iftd[2]), cls.iftd_byte_3_descriptions, True
) )
)
fbz = value[1+len(iftd):]
if len(fbz) == 2:
result.append("\tVerification failure counter (FBZ): %s" % utils.hexdump(fbz, short=True))
if fbz == "\x00\x00":
result.append("\t\tFBZ unused")
else:
result.append("\t\tCurrent value: %i (0x%x)%s" % (
ord(fbz[0]), ord(fbz[0]),
ord(fbz[0]) == 0 and (ord(fbz[1]) != 0 and " (Secret locked)" or " (FBZ unused)") or "")
)
resetmode = ord(fbz[1])
result.append("\t\tReset value: %i (0x%x)%s" % (
resetmode & 0x7F, resetmode & 0x7F,
resetmode == 0 and " (FBZ unused)" or (
resetmode & 0x80 == 0x00 and " (reset with unblock password and successful verification)"
or " (reset only with unblock password)")
)
)
return "\n".join(result)
# This is similar to MTCOS_Card.decode_security_attributes but not identical
def decode_security_attributes(value):
results = []
if len(value) == 6:
results.append( " " + utils.hexdump(value, short=True) )
else:
results.append("")
for i in range(len(value)/6):
part = value[i*6:i*6+6]
partresponse = []
if len(value) != 6:
partresponse.append("Rule: %s\n" % utils.hexdump(part, short=True))
if ord(part[0])&0xFE == 0x60:
partresponse.append("Admin commands")
else:
partresponse.append("Command 0x%02X" % (ord(part[0])&0xFE) )
all = not (ord(part[0])&0x01)
secrets = []
b2 = ord(part[1])
for k in range(4):
if b2 & (0x10<<k):
secrets.append("global password with number %s" % hex(k) )
for k in range(4):
if b2 & (0x01<<k):
secrets.append("local password with number %s" % hex(k) )
b3 = ord(part[2])
for k in range(8):
if b3 & (0x01<<k):
secrets.append("global key with number %s" % k)
b4 = ord(part[3])
for k in range(8):
if b4 & (0x01<<k):
secrets.append("local key with number %s" % k)
if len(secrets) > 1:
partresponse.append(
" needs\n\t " + (all and "\n\tAND " or "\n\t OR ").join(secrets)
)
elif len(secrets) == 1:
partresponse.append(" needs " + secrets[0])
elif len(secrets) == 0:
partresponse.append(" always allowed")
def decode_key(value):
partresponse.append( (value&0x80) and "local" or "global" )
partresponse.append(" key, ")
partresponse.append( (value&0x40) and "random" or "any" )
partresponse.append(" IV")
if not (value & 0x20):
partresponse.append(", key with number: ")
if (value & 0x1F) != 0x1F:
partresponse.append("0x%02x" % (value & 0x1F) )
else:
partresponse.append("RFU")
b5 = ord(part[4])
b6 = ord(part[5])
if b5 == 0xff and b6 == 0xff and len(secrets) <= 1:
partresponse.append(", No secure messaging required")
else:
if b5 == 0xff:
partresponse.append("\nSecure messaging: no MAC required")
else:
partresponse.append("\nCryptographic MAC with ")
decode_key(b5)
if b6 == 0xff:
partresponse.append("\nSecure messaging: no encryption required")
elif not (b6 & 0x20):
partresponse.append("\nEncryption with ")
decode_key(b6)
else:
partresponse.append("\nEncryption: RFU")
if len(value) != 6:
results.append("\n\t".join("".join(partresponse).splitlines()))
else:
results.append("".join(partresponse))
return "\n".join(results)
def __init__(self, *args, **kwargs):
ISO_7816_4_Card.__init__(self,*args,**kwargs)
self.cmd_clear_se()
def cmd_clear_se(self):
"Reset the host security environment"
self.se = TCOS_Security_Environment(self)
def cmd_set_key(self, ref, key, *args):
"Set a key in the host security environment"
self.se.set_key( int(ref,0), binascii.a2b_hex( "".join( (key + "".join(args)).split() ) ) )
def delete_file(self, fid):
result = self.send_apdu(
C_APDU(self.APDU_DELETE_FILE, data = fid)
)
return result
def cmd_delete(self, file):
"Delete a file"
fid = binascii.a2b_hex("".join(file.split()))
self.delete_file(fid)
def before_send(self, apdu):
return self.se.before_send(apdu)
def after_send(self, result):
return self.se.after_send(result)
def decode_file_descriptor_extension_HACK(*args, **kwargs): return TCOS_Card.decode_file_descriptor_extension(*args, **kwargs)
TLV_OBJECTS = {
TLV_utils.context_FCP: {
0x86: (decode_security_attributes, "Security attributes"),
0x85: (decode_file_descriptor_extension_HACK, "File descriptor extension"),
},
}
TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]
COMMANDS = {
"list_dirs": building_blocks.Card_with_80_aa.cmd_listdirs,
"list_files": building_blocks.Card_with_80_aa.cmd_listfiles,
"ls": building_blocks.Card_with_80_aa.cmd_list,
"delete": cmd_delete,
"clear_se": cmd_clear_se,
"set_key": cmd_set_key,
}
TLV_utils.identifier("context_ardo")
TLV_utils.identifier("context_art")
def decode_access_rule(mask, value):
result = []
for i in range(3, -1, -1):
if mask & (1<<i):
result.append(binascii.b2a_hex(value[0]))
value = value[1:]
else:
result.append("__");
return " " + " ".join(result)
compact_access_descriptions = (
(0xc0, 0xc0, None, "proprietary"),
(0xa0, 0xa0, None, "proprietary"),
(0x90, 0x90, None, "proprietary"),
(0x88, 0x88, None, "proprietary"),
(0xc0, 0x40, None, "DF: DELETE FILE(self); EF: DELETE FILE; DO: RFU"),
(0xa0, 0x20, None, "DF: TERMINATE MF / DF; EF: TERMINATE EF; DO: RFU"),
(0x90, 0x10, None, "DF: ACTIVATE FILE; EF: ACTIVATE FILE; DO: RFU"),
(0x88, 0x08, None, "DF: DEACTIVATE FILE; EF: DEACTIVATE FILE; DO: RFU"),
(0x04, 0x04, None, "DF: CREATE FILE (DF); EF: APPEND RECORD; DO: MANAGE SECURITY ENVIRONMENT"),
(0x02, 0x02, None, "DF: CREATE FILE (EF); EF: UPDATE BINARY/RECORD; DO: PUT DATA"),
(0x01, 0x01, None, "DF: DELETE FILE (child); EF: READ/SEARCH BINARY/RECORD; DO: GET DATA"),
)
def decode_compact_access_bitmap(value):
return (" %s \n" % utils.hexdump(value, short=True)) + "\n\t".join(utils.parse_binary(ord(value[0]), compact_access_descriptions, True))
class TCOS_3_Card(TCOS_Card):
DRIVER_NAME = ["TCOS 3.0"]
APDU_DELETE_FILE = C_APDU(cla=0x80,ins=0xe4)
SELECT_P2 = 0x04
LS_L_SIZE_TAG = 0x80
ATRS = [
("3bbf.6008131fe5d0064........31c073f701d0009000..", None),
]
file_status_descriptions = (
(0xe0, 0x00, "RFU", "Data file"),
(0x1f, 0x00, None, "General data file"),
(0x1f, 0x01, None, "EF_ATR"),
(0x1f, 0x02, None, "EF_GDO"),
(0x1f, 0x03, None, "EF_TranslateDO"),
(0x1f, 0x04, None, "EF_Key"),
(0x1f, 0x05, None, "EF_KeyD"),
(0x1f, 0x06, None, "EF_PWD"),
(0x1f, 0x07, None, "EF_PWDD"),
(0x1f, 0x08, None, "EF_CERTD"),
(0x1f, 0x09, None, "EF_DO"),
(0x1f, 0x0a, None, "EF_FCI"),
(0x1f, 0x0b, None, "EF_Rule"),
(0x1f, 0x0c, None, "EF_SE"),
(0x1f, 0x0d, None, "RFU"),
(0x1f, 0x0e, None, "RFU"),
(0x1f, 0x0f, None, "RFU"),
(0x10, 0x10, None, "RFU"),
)
def decode_file_descriptor_extension_HACK(*args, **kwargs): return TCOS_3_Card.decode_file_descriptor_extension(*args, **kwargs)
TLV_OBJECTS = {
TLV_utils.context_FCP: {
0x86: (TCOS_Card.decode_security_attributes, "Security attributes"),
0x85: (decode_file_descriptor_extension_HACK, "File descriptor extension"),
0xAB: (TLV_utils.recurse, "Access rule object", context_ardo),
0xA2: (TLV_utils.recurse, "SFI / Path mapping template", None),
0xA0: (TLV_utils.recurse, "Access rule template for data objects", context_art),
0xA1: (TLV_utils.recurse, "Interface mode template", None),
},
context_ardo: {
0x80: (decode_compact_access_bitmap, "Compact bitmap"),
0x81: (lambda a: decode_access_rule(1, a), "APDU equals"),
0x82: (lambda a: decode_access_rule(2, a), "APDU equals"),
0x83: (lambda a: decode_access_rule(3, a), "APDU equals"),
0x84: (lambda a: decode_access_rule(4, a), "APDU equals"),
0x85: (lambda a: decode_access_rule(5, a), "APDU equals"),
0x86: (lambda a: decode_access_rule(6, a), "APDU equals"),
0x87: (lambda a: decode_access_rule(7, a), "APDU equals"),
0x88: (lambda a: decode_access_rule(8, a), "APDU equals"),
0x89: (lambda a: decode_access_rule(9, a), "APDU equals"),
0x8a: (lambda a: decode_access_rule(0xa, a), "APDU equals"),
0x8b: (lambda a: decode_access_rule(0xb, a), "APDU equals"),
0x8c: (lambda a: decode_access_rule(0xc, a), "APDU equals"),
0x8d: (lambda a: decode_access_rule(0xd, a), "APDU equals"),
0x8e: (lambda a: decode_access_rule(0xe, a), "APDU equals"),
0x8f: (lambda a: decode_access_rule(0xf, a), "APDU equals"),
0x90: (None, "Always allowed"),
0x97: (None, "Never allowed"),
},
context_art: {
0xAB: (TLV_utils.recurse, "Access rule object", context_ardo),
0x5C: (TLV_utils.binary, "Tag list"),
}
}
TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]