import utils, TLV_utils from iso_7816_4_card import * import building_blocks MODE_ECB = 0 MODE_CBC = 1 ALGO_IDEA = 0x1 ALGO_DES = 0x2 ALGO_DES3 = 0x3 SE_APDU = 1 SE_RAPDU = 2 SE_PSO = 3 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 if config is not None: self.parse(config) def parse(self, config): structure = TLV_utils.unpack(config) for tag, length, value in structure: if tag == 0x80: self.mode = ord(value[0]) & 1 algorithm = (ord(value[0]) >> 2) & 0x7 if algorithm not in (ALGO_DES, ALGO_DES3, ALGO_IDEA): raise ValueError, "Malformed cipher algorithm (tag 0x80)" 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: raise ValueError, "Malformed MSE parameters (tag 0x%02x)" % tag class TCOS_Security_Environment(object): def __init__(self, card): self.keys = {} self.card = card self.last_c_apdu = None self.last_r_apdu = None self.config = {} def get_config(self, context, operation): if not self.config.has_key( (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 def before_send(self, apdu): self.last_c_apdu = apdu return apdu def after_send(self, result): self.last_r_apdu = result if result.sw == self.card.SW_OK: if (self.last_c_apdu.cla & 0xf0) == 0x00: if self.last_c_apdu.ins == 0x22: self.parse_mse(self.last_c_apdu) return result 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" APDU_DELETE_FILE = C_APDU(cla=0x80,ins=0xe4) 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< 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) 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, "clear_se": cmd_clear_se, "set_key": cmd_set_key, }