diff --git a/cards/gsm_card.py b/cards/gsm_card.py index e00d1a1..ec5c6a3 100644 --- a/cards/gsm_card.py +++ b/cards/gsm_card.py @@ -1,32 +1,291 @@ import utils from iso_card import * +import building_blocks -class GSM_Card(ISO_Card): +class GSM_Card(building_blocks.Card_with_read_binary,ISO_Card): DRIVER_NAME = ["GSM"] COMMAND_GET_RESPONSE = C_APDU("\xa0\xC0\x00\x00") - + CLA = 0xA0 + APDU_GET_RESPONSE = C_APDU(cla=CLA,ins=0xC0) + APDU_RUN_GSM_ALGO = C_APDU(cla=CLA,ins=0x88) + APDU_VERIFY_PIN = C_APDU(cla=CLA,ins=0x20) + + APDU_SELECT_FILE = C_APDU(cla=CLA,ins=0xA4) + APDU_READ_BINARY = C_APDU(cla=CLA,ins=0xB0) + APDU_STATUS = C_APDU(cla=CLA,ins=0xF2) + APDU_UPD_BINARY = C_APDU(cla=CLA,ins=0xD6) + APDU_UPD_RECORD = C_APDU(cla=CLA,ins=0xDC) + + # TS 11.14 / STK related + APDU_TERMINAL_PROFILE = C_APDU(cla=CLA,ins=0x10) + APDU_ENVELOPE = C_APDU(cla=CLA,ins=0xC2) + APDU_FETCH = C_APDU(cla=CLA,ins=0x12) + APDU_TERMINAL_RESPONSE = C_APDU(cla=CLA,ins=0x14) + STATUS_MAP = { Card.PURPOSE_GET_RESPONSE: ("9F??", ) } - + + INTERESTING_DFS = [ + ("DF.GSM", "\x7f\x20"), + ("DF.TELECOM", "\x7f\x10"), + ] + + # Files at the GSM application level + GSM_DFS = [ + ("LP", "\x6f\x05", "Language Preference"), + ("IMSI", "\x6f\x07", "IMSI"), + ("Kc","\x6f\x20", "Ciphering Key Kc"), + ("PLMNsel", "\x6f\x30", "PLMN selector"), + ("HPPLMN", "\x6f\x31", "Higher Priority PLMN search period"), + ("ACMmax", "\x6f\x37", "ACM maximum value"), + ("SST", "\x6f\x38", "SIM service table"), + ("ACM", "\x6f\x39", "Accumulated call meter"), + ("GID1", "\x6f\x3e", "Group Identifier Level 1"), + ("GID2", "\x6f\x3f", "Group Identifier Level 2"), + ("SPN", "\x6f\x46", "Service Provider Name"), + ("PUCT", "\x6f\x41", "Price per unit and currency table"), + ("CBMI", "\x6f\x45", "Cell broadcast message identifier selection"), + ("BCCH", "\x6f\x74", "Broadcast control channels"), + ("ACC", "\x6f\x78", "Access control class"), + ("FPLMN", "\x6f\x7b", "Forbidden PLMNs"), + ("LOCI", "\x6f\x7e", "Location Information"), + ("AD", "\x6f\xAD", "Administrative Data"), + ("Phase", "\x6f\xAE", "Phase identification"), + ("VGCS", "\x6f\xB1", "Voice Group Call Service"), + ("VGCSS", "\x6f\xB2", "Voice Group Call Service Status"), + ("VBS", "\x6f\xB3", "Voice Broadcast Service"), + ("VBSS", "\x6f\xB4", "Voice Broadcast Service Status"), + ("eMLPP", "\x6f\xB5", "enhanced Multi Level Pre-emptyion and Priority"), + ("AAeM", "\x6f\xB5", "Authomatic Answer for eEMLPP Service"), + ("CBMID", "\x6f\x48", "Cell Broadcast Message Identifier for Data Download"), + ("ECC", "\x6f\xB7", "Emergency Call Codes"), + ("CBMIR", "\x6f\x50", "Cell broadcast message identifier range selection"), + ("DCK", "\x6f\x2C", "De-personalization Control Keys"), + ("CNL", "\x6f\x32", "Co-operative Network List"), + ("NIA", "\x6f\x51", "Network's Indication of Alerting"), + ("KcGPRS", "\x6f\x52", "GPRS Ciphering Key KcGPRS"), + ("LOCIGPRS", "\x6f\x53", "GPRS Location Information"), + ("SUME", "\x6f\x54", "SetUpMenu Elements"), + ("PLMNwAcT", "\x6f\x60", "User controlled PLMN Selector with Access Technology"), + ("OPLMNwAcT", "\x6f\x51", "Operator controlled PLMN Selector with Access Technology"), + ("HPLMNwAcT", "\x6f\x62", "HPLMN Selector with Access Technology"), + ("CPBCCH", "\x6f\x63", "CPBCCH Information"), + ("InvScan", "\x6f\x64", "Investigation Scan"), + ] + + # According to TS 11.11 Chapter 9.3 + type_of_file_names = { 0: 'RFU', 1: 'MF', 2: 'DF', 4: 'EF' } + struct_of_file_names = { 0: 'transparent', + 1: 'linear fixed', + 2: 'transparent', + 3: 'cyclic' } + acc_cond_names = { 0: 'ALWAYS', + 1: 'CHV1', + 2: 'CHV2', + 3: 'RFU', + 4: 'ADM1', + 5: 'ADM2', + 6: 'ADM3', + 7: 'ADM4', + 8: 'ADM5', + 9: 'ADM6', + 10: 'ADM7', + 11: 'ADM8', + 12: 'ADM9', + 13: 'ADM10', + 14: 'ADM11', + 15: 'NEW' } ATRS = [ ("3bff9500ffc00a1f438031e073f62113574a334861324147d6", None), + ("3b9a940092027593110001020200", None), + ("3b989400939114010c020102", None), + #("3b991800118822334455667760", None), + #("3bdf96ff80b1fe451fc78031e073fe21136791150103040404ef", None), ] + + def before_send(self, apdu): + if apdu.cla == 0x00: + apdu.CLA = self.CLA + + return apdu + + def cmd_run_gsm_algo(self, rand): + """Perform the GSM A3/A8 algorithm. + RAND is the random challenge to be sent to the card.""" + if len(rand) != 16: + rand2 = binascii.a2b_hex("".join(rand.split())) + else: + rand2 = rand + + if len(rand2) != 16: + raise TypeError, "Need either exactly 16 binary bytes or 16 hexedecimal bytes for the RAND argument." + + apdu = C_APDU(self.APDU_RUN_GSM_ALGO, + p1 = 0, p2 = 0, data = rand2) + + result = self.send_apdu(apdu) + + return result + + def cmd_cd_gsm(self): + """Change into DF.GSM.""" + apdu = C_APDU(self.APDU_SELECT_FILE, data = "\x7f\x20") + result = self.send_apdu(apdu) + return result + +# def cmd_cd(self, dir = None): +# fid = None +# for n, f in self.INTERESTING_FILES: +# if n == dir: +# fid = f +# break +# if fid is None: +# return ISO_7816_4_Card.cmd_cd(self, dir) +# else: +# return ISO_7816_4_Card.change_dir(self, fid) + + def change_dir(self, fid = None): + "Change to a child DF. Alternatively, change to MF if fid is None." + if fid is None: + return self.select_file(0, 0, "\x3f\x00") + else: + return self.select_file(0, 0, fid) + + def cmd_status(self): + """STATUS Command.""" + apdu = C_APDU(self.APDU_STATUS, data="\xff") + result = self.send_apdu(apdu) + if len(result.data) > 0: + print utils.hexdump(result.data) + print self.sel_ret_decode(result.data) + + def cmd_term_prof(self, data): + """TERMINAL PROFILE Command.""" + apdu = C_APDU(self.APDU_TERMINAL_PROFILE, data = data) + return self.send_apdu(apdu) + + def cmd_envelope(self, data): + """ENVELOPE Command.""" + apdu = C_APDU(self.APDU_ENVELOPE, data = data) + return self.send_apdu(apdu) + + def cmd_fetch(self, data): + """FETCH Command.""" + apdu = C_APDU(self.APDU_FETCH, data = data) + return self.send_apdu(apdu) + + def cmd_term_resp(self, data): + """TERMINAL RESPONSE Command.""" + apdu = C_APDU(self.APDU_TERMINAL_RESPONSE, data = data) + return self.send_apdu(apdu) + + def cmd_upd_binary(self, data): + """Write to a transparent binary file.""" + data_bin = binascii.a2b_hex("".join(data.split())) + apdu = C_APDU(self.APDU_UPD_BINARY, data = data_bin) + return self.send_apdu(apdu) + + def cmd_selectfile(self, fid): + """Select a file on the card.""" + + fid = binascii.a2b_hex("".join(fid.split())) + + result = self.select_file(0, 0, fid) + if len(result.data) > 0: + print utils.hexdump(result.data) + print self.sel_ret_decode(result.data) + + def sel_ret_decode(self, data): + #print "File: %s" % (data[ + type_of_file = ord(data[6]) + print "Type of File: %s" % (self.type_of_file_names[type_of_file]) + if type_of_file == 4: + structure = ord(data[12]) + print "Structure of File: %s" % (self.struct_of_file_names[structure]) + if structure == 3: + if ord(data[7]) & 0x80: + print "INCREASE allowed" + else: + print "INCREASE not allowed" + if structure == 1 or structure == 3: + print "Record size: %u bytes" % ord(data[14]) + #print "File Size: %u bytes" % + acc_cond = data[8:11] + self.acc_cond_decode(acc_cond) + status = ord(data[11]) + if not status & 0x1: + print "Status: invalidated, " + if not status & 0x4: + print "not " + print "readable and updatable\n" + elif type_of_file == 2: + #print "Total unallocated memory in DF: %u bytes" + gsm_spec_data = data[13:] + print "Number of DFs : %u" % (ord(gsm_spec_data[1])) + print "Number of EFs : %u" % (ord(gsm_spec_data[2])) + print "Number of CHV : %u" % (ord(gsm_spec_data[3])) + print "CHV1 status : %s" % (self.chv_status_decode(gsm_spec_data[5])) + print "UNBLOCK CHV1 status : %s" % (self.chv_status_decode(gsm_spec_data[6])) + print "CHV2 status : %s" % (self.chv_status_decode(gsm_spec_data[7])) + print "UNBLOCK CHV2 status : %s" % (self.chv_status_decode(gsm_spec_data[8])) + + def chv_status_decode(self, s): + status = ord(s) + if status & 0x80: + initialized = 1 + else: + initialized = 0 + return "Initialized: %u, Retries remaining: %u" % (initialized, status & 0xf) + + def acc_cond_decode(self, acc_cond): + print "Access cond. READ/SEEK : %s" % (self.acc_cond_names[ord(acc_cond[0]) >> 4]) + print "Access cond. UPDATE : %s" % (self.acc_cond_names[ord(acc_cond[0]) & 0xf]) + print "Access cond. INCREASE : %s" % (self.acc_cond_names[ord(acc_cond[1]) >> 4]) + print "Access cond. REHABILITATE: %s" % (self.acc_cond_names[ord(acc_cond[2]) >> 4]) + print "Access cond. INVALIDATE : %s" % (self.acc_cond_names[ord(acc_cond[2]) & 0xf]) + + + COMMANDS = dict(Card.COMMANDS) + COMMANDS.update(building_blocks.Card_with_read_binary.COMMANDS) + COMMANDS.update({ + "gsm_run_algo" : cmd_run_gsm_algo, + #"cd" : cmd_cd, + "cd_df_gsm" : cmd_cd_gsm, + "gsm_status" : cmd_status, + # STK + "gsm_terminal_profile" : cmd_term_prof, + "gsm_envelope" : cmd_envelope, + "gsm_fetch" : cmd_fetch, + "gsm_terminal_response" : cmd_term_resp, + "gsm_select_file" : cmd_selectfile, + "update_binary" : cmd_upd_binary, + }) STATUS_WORDS = { + # TS 11.11 Chapter 9.4.1 + #'9000': "Normal ending of the command" + #'91??': "Normal ending of the command, with extra information from proactive SIM" + '9E??': "Length '$(SW2)i (0x$(SW2)02x)' of the response data in case of SIM data dl error", '9F??': "Length '%(SW2)i (0x%(SW2)02x)' of the response data", + # TS 11.11 Chapter 9.4.2 + '9300': "SIM Application Toolkit busy", + # TS 11.11 Chapter 9.4.3 '920?': lambda sw1, sw2: "Update successful but after using an internal retry routine '%i' times" % (sw2 % 16), '9240': "Memory problem", + # TS 11.11 Chapter 9.4.4 '9400': "No EF selected", '9402': "Out of range (invalid address)", '9404': "- File ID not found\n- Pattern not found", '9408': "File is inconsistent with the command", + # TS 11.11 Chapter 9.4.5 '9802': "No CHV initialized", '9804': "- Access condition not fulfilled\n- Unsuccessful CHV verification, at least one attempt left\n- unsuccesful UNBLOCK CHV verification, at least one attempt left\n- authentication failed", '9808': "In contradiction with CHV status", '9810': "In contradiction with invalidation status", '9840': "- Unsuccessful CHV verification, no attempt left\n- unsuccesful UNBLOCK CHV verification, no attempt left\n- CHV blocked\n- UNBLOCK CHV blocked", '9850': "Increase cannot be performed, Max value reached", + # TS 11.11 Chapter 9.4.6 "67??": "Incorrect parameter P3", "\x67\x00": "Incorrect parameter P3 (ISO:Wrong length)", "6B??": "Incorrect parameter P1 or P2",