Extend the GSM card module with lots of SIM card commands
including STATUS, TERMINAL PROFILE, TERMINAL RESPONSE, UPDATE BINARY,...
This commit is contained in:
parent
14819e9661
commit
8483d8a7ec
|
@ -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??", )
|
||||
}
|
||||
|
||||
ATRS = [
|
||||
("3bff9500ffc00a1f438031e073f62113574a334861324147d6", None),
|
||||
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",
|
||||
|
|
Loading…
Reference in New Issue