224 lines
8.6 KiB
Python
224 lines
8.6 KiB
Python
import utils, TLV_utils
|
|
from iso_7816_4_card import *
|
|
import building_blocks
|
|
|
|
class TCOS_Card(ISO_7816_4_Card,building_blocks.Card_with_80_aa):
|
|
DRIVER_NAME = "TCOS"
|
|
APDU_DELETE_FILE = C_APDU("\x80\xe4\x00\x00")
|
|
|
|
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),
|
|
)
|
|
def decode_file_descriptor_extension(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]), TCOS_Card.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]), TCOS_Card.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]), TCOS_Card.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 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)
|
|
|
|
TLV_OBJECTS = {
|
|
TLV_utils.context_FCP: {
|
|
0x86: (decode_security_attributes, "Security attributes"),
|
|
0x85: (decode_file_descriptor_extension, "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,
|
|
}
|
|
|