mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-03-27 15:58:34 +03:00
cosmetic: Switch to consistent four-spaces indent; run autopep8
We had a mixture of tab and 4space based indenting, which is a bad idea. 4space is the standard in python, so convert all our code to that. The result unfortuantely still shoed even more inconsistencies, so I've decided to run autopep8 on the entire code base. Change-Id: I4a4b1b444a2f43fab05fc5d2c8a7dd6ddecb5f07
This commit is contained in:
@@ -41,6 +41,7 @@ from pySim.ts_51_011 import EF, EF_AD
|
|||||||
from pySim.card_handler import *
|
from pySim.card_handler import *
|
||||||
from pySim.utils import *
|
from pySim.utils import *
|
||||||
|
|
||||||
|
|
||||||
def parse_options():
|
def parse_options():
|
||||||
|
|
||||||
parser = OptionParser(usage="usage: %prog [options]")
|
parser = OptionParser(usage="usage: %prog [options]")
|
||||||
@@ -190,7 +191,6 @@ def parse_options():
|
|||||||
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
|
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
|
||||||
help="Read parameters from CSV file rather than command line")
|
help="Read parameters from CSV file rather than command line")
|
||||||
|
|
||||||
|
|
||||||
parser.add_option("--write-csv", dest="write_csv", metavar="FILE",
|
parser.add_option("--write-csv", dest="write_csv", metavar="FILE",
|
||||||
help="Append generated parameters in CSV file",
|
help="Append generated parameters in CSV file",
|
||||||
)
|
)
|
||||||
@@ -215,12 +215,14 @@ def parse_options():
|
|||||||
|
|
||||||
if options.source == 'csv':
|
if options.source == 'csv':
|
||||||
if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False) and (options.read_iccid is False):
|
if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False) and (options.read_iccid is False):
|
||||||
parser.error("CSV mode needs either an IMSI, --read-imsi, --read-iccid or batch mode")
|
parser.error(
|
||||||
|
"CSV mode needs either an IMSI, --read-imsi, --read-iccid or batch mode")
|
||||||
if options.read_csv is None:
|
if options.read_csv is None:
|
||||||
parser.error("CSV mode requires a CSV input file")
|
parser.error("CSV mode requires a CSV input file")
|
||||||
elif options.source == 'cmdline':
|
elif options.source == 'cmdline':
|
||||||
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
|
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
|
||||||
parser.error("If either IMSI or ICCID isn't specified, num is required")
|
parser.error(
|
||||||
|
"If either IMSI or ICCID isn't specified, num is required")
|
||||||
else:
|
else:
|
||||||
parser.error("Only `cmdline' and `csv' sources supported")
|
parser.error("Only `cmdline' and `csv' sources supported")
|
||||||
|
|
||||||
@@ -232,7 +234,8 @@ def parse_options():
|
|||||||
|
|
||||||
if (options.batch_mode):
|
if (options.batch_mode):
|
||||||
if (options.imsi is not None) or (options.iccid is not None):
|
if (options.imsi is not None) or (options.iccid is not None):
|
||||||
parser.error("Can't give ICCID/IMSI for batch mode, need to use automatic parameters ! see --num and --secret for more informations")
|
parser.error(
|
||||||
|
"Can't give ICCID/IMSI for batch mode, need to use automatic parameters ! see --num and --secret for more information")
|
||||||
|
|
||||||
if args:
|
if args:
|
||||||
parser.error("Extraneous arguments")
|
parser.error("Extraneous arguments")
|
||||||
@@ -246,15 +249,19 @@ def _digits(secret, usage, len, num):
|
|||||||
d = ''.join(['%02d' % x for x in s.digest()])
|
d = ''.join(['%02d' % x for x in s.digest()])
|
||||||
return d[0:len]
|
return d[0:len]
|
||||||
|
|
||||||
|
|
||||||
def _mcc_mnc_digits(mcc, mnc):
|
def _mcc_mnc_digits(mcc, mnc):
|
||||||
return '%s%s' % (mcc, mnc)
|
return '%s%s' % (mcc, mnc)
|
||||||
|
|
||||||
|
|
||||||
def _cc_digits(cc):
|
def _cc_digits(cc):
|
||||||
return ('%03d' if cc > 100 else '%02d') % cc
|
return ('%03d' if cc > 100 else '%02d') % cc
|
||||||
|
|
||||||
|
|
||||||
def _isnum(s, l=-1):
|
def _isnum(s, l=-1):
|
||||||
return s.isdigit() and ((l == -1) or (len(s) == l))
|
return s.isdigit() and ((l == -1) or (len(s) == l))
|
||||||
|
|
||||||
|
|
||||||
def _ishex(s, l=-1):
|
def _ishex(s, l=-1):
|
||||||
hc = '0123456789abcdef'
|
hc = '0123456789abcdef'
|
||||||
return all([x in hc for x in s.lower()]) and ((l == -1) or (len(s) == l))
|
return all([x in hc for x in s.lower()]) and ((l == -1) or (len(s) == l))
|
||||||
@@ -272,7 +279,8 @@ def _dbi_binary_quote(s):
|
|||||||
for i in range(1, 256):
|
for i in range(1, 256):
|
||||||
if i == 39:
|
if i == 39:
|
||||||
continue
|
continue
|
||||||
sum_ = cnt.get(i, 0) + cnt.get((i+1)&0xff, 0) + cnt.get((i+39)&0xff, 0)
|
sum_ = cnt.get(i, 0) + cnt.get((i+1) & 0xff, 0) + \
|
||||||
|
cnt.get((i+39) & 0xff, 0)
|
||||||
if sum_ < m:
|
if sum_ < m:
|
||||||
m = sum_
|
m = sum_
|
||||||
e = i
|
e = i
|
||||||
@@ -292,6 +300,7 @@ def _dbi_binary_quote(s):
|
|||||||
|
|
||||||
return ''.join(out)
|
return ''.join(out)
|
||||||
|
|
||||||
|
|
||||||
def gen_parameters(opts):
|
def gen_parameters(opts):
|
||||||
"""Generates Name, ICCID, MCC, MNC, IMSI, SMSP, Ki, PIN-ADM from the
|
"""Generates Name, ICCID, MCC, MNC, IMSI, SMSP, Ki, PIN-ADM from the
|
||||||
options given by the user"""
|
options given by the user"""
|
||||||
@@ -331,7 +340,8 @@ def gen_parameters(opts):
|
|||||||
'Start with \'+\' for international numbers.')
|
'Start with \'+\' for international numbers.')
|
||||||
if len(msisdn) > 10 * 2:
|
if len(msisdn) > 10 * 2:
|
||||||
# TODO: Support MSISDN of length > 20 (10 Bytes)
|
# TODO: Support MSISDN of length > 20 (10 Bytes)
|
||||||
raise ValueError('MSISDNs longer than 20 digits are not (yet) supported.')
|
raise ValueError(
|
||||||
|
'MSISDNs longer than 20 digits are not (yet) supported.')
|
||||||
|
|
||||||
# ICCID (19 digits, E.118), though some phase1 vendors use 20 :(
|
# ICCID (19 digits, E.118), though some phase1 vendors use 20 :(
|
||||||
if opts.iccid is not None:
|
if opts.iccid is not None:
|
||||||
@@ -406,7 +416,8 @@ def gen_parameters(opts):
|
|||||||
else:
|
else:
|
||||||
smsc = '00%d' % opts.country + '5555' # Hack ...
|
smsc = '00%d' % opts.country + '5555' # Hack ...
|
||||||
|
|
||||||
smsc = '%02d' % ((len(smsc) + 3)//2,) + ton + swap_nibbles(rpad(smsc, 20))
|
smsc = '%02d' % ((len(smsc) + 3)//2,) + ton + \
|
||||||
|
swap_nibbles(rpad(smsc, 20))
|
||||||
|
|
||||||
smsp = (
|
smsp = (
|
||||||
'e1' + # Parameters indicator
|
'e1' + # Parameters indicator
|
||||||
@@ -456,7 +467,8 @@ def gen_parameters(opts):
|
|||||||
epdg_mcc = opts.epdgSelection[:3]
|
epdg_mcc = opts.epdgSelection[:3]
|
||||||
epdg_mnc = opts.epdgSelection[3:]
|
epdg_mnc = opts.epdgSelection[3:]
|
||||||
if not epdg_mcc.isdigit() or not epdg_mnc.isdigit():
|
if not epdg_mcc.isdigit() or not epdg_mnc.isdigit():
|
||||||
raise ValueError('PLMN for ePDG Selection must only contain decimal digits')
|
raise ValueError(
|
||||||
|
'PLMN for ePDG Selection must only contain decimal digits')
|
||||||
|
|
||||||
# Return that
|
# Return that
|
||||||
return {
|
return {
|
||||||
@@ -511,6 +523,7 @@ def write_params_csv(opts, params):
|
|||||||
cw.writerow([params[x] for x in row])
|
cw.writerow([params[x] for x in row])
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def _read_params_csv(opts, iccid=None, imsi=None):
|
def _read_params_csv(opts, iccid=None, imsi=None):
|
||||||
import csv
|
import csv
|
||||||
f = open(opts.read_csv, 'r')
|
f = open(opts.read_csv, 'r')
|
||||||
@@ -539,6 +552,7 @@ def _read_params_csv(opts, iccid=None, imsi=None):
|
|||||||
f.close()
|
f.close()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def read_params_csv(opts, imsi=None, iccid=None):
|
def read_params_csv(opts, imsi=None, iccid=None):
|
||||||
row = _read_params_csv(opts, iccid=iccid, imsi=imsi)
|
row = _read_params_csv(opts, iccid=iccid, imsi=imsi)
|
||||||
if row is not None:
|
if row is not None:
|
||||||
@@ -565,9 +579,11 @@ def read_params_csv(opts, imsi=None, iccid=None):
|
|||||||
try:
|
try:
|
||||||
try_encode = h2b(pin_adm)
|
try_encode = h2b(pin_adm)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("pin_adm_hex needs to be hex encoded using this option")
|
raise ValueError(
|
||||||
|
"pin_adm_hex needs to be hex encoded using this option")
|
||||||
else:
|
else:
|
||||||
raise ValueError("pin_adm_hex needs to be exactly 16 digits (hex encoded)")
|
raise ValueError(
|
||||||
|
"pin_adm_hex needs to be exactly 16 digits (hex encoded)")
|
||||||
|
|
||||||
return row
|
return row
|
||||||
|
|
||||||
@@ -597,12 +613,14 @@ def write_params_hlr(opts, params):
|
|||||||
'(subscriber_id, algorithm_id, a3a8_ki)' +
|
'(subscriber_id, algorithm_id, a3a8_ki)' +
|
||||||
'VALUES ' +
|
'VALUES ' +
|
||||||
'(?,?,?)',
|
'(?,?,?)',
|
||||||
[ sub_id, 2, sqlite3.Binary(_dbi_binary_quote(h2b(params['ki']))) ],
|
[sub_id, 2, sqlite3.Binary(
|
||||||
|
_dbi_binary_quote(h2b(params['ki'])))],
|
||||||
)
|
)
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def write_parameters(opts, params):
|
def write_parameters(opts, params):
|
||||||
write_params_csv(opts, params)
|
write_params_csv(opts, params)
|
||||||
write_params_hlr(opts, params)
|
write_params_hlr(opts, params)
|
||||||
@@ -611,6 +629,7 @@ def write_parameters(opts, params):
|
|||||||
BATCH_STATE = ['name', 'country', 'mcc', 'mnc', 'smsp', 'secret', 'num']
|
BATCH_STATE = ['name', 'country', 'mcc', 'mnc', 'smsp', 'secret', 'num']
|
||||||
BATCH_INCOMPATIBLE = ['iccid', 'imsi', 'ki']
|
BATCH_INCOMPATIBLE = ['iccid', 'imsi', 'ki']
|
||||||
|
|
||||||
|
|
||||||
def init_batch(opts):
|
def init_batch(opts):
|
||||||
# Need to do something ?
|
# Need to do something ?
|
||||||
if not opts.batch_mode:
|
if not opts.batch_mode:
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ option_parser = argparse.ArgumentParser(prog='pySim-read',
|
|||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
argparse_add_reader_args(option_parser)
|
argparse_add_reader_args(option_parser)
|
||||||
|
|
||||||
|
|
||||||
def select_app(adf: str, card: SimCard):
|
def select_app(adf: str, card: SimCard):
|
||||||
"""Select application by its AID"""
|
"""Select application by its AID"""
|
||||||
sw = 0
|
sw = 0
|
||||||
@@ -63,6 +64,7 @@ def select_app(adf:str, card:SimCard):
|
|||||||
|
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
# Parse options
|
# Parse options
|
||||||
@@ -270,7 +272,8 @@ if __name__ == '__main__':
|
|||||||
if usim_card.file_exists(EF_USIM_ADF_map['ePDGId']):
|
if usim_card.file_exists(EF_USIM_ADF_map['ePDGId']):
|
||||||
(res, sw) = usim_card.read_epdgid()
|
(res, sw) = usim_card.read_epdgid()
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
print("ePDGId:\n%s" % (len(res) and res or '\tNot available\n',))
|
print("ePDGId:\n%s" %
|
||||||
|
(len(res) and res or '\tNot available\n',))
|
||||||
else:
|
else:
|
||||||
print("ePDGId: Can't read, response code = %s" % (sw,))
|
print("ePDGId: Can't read, response code = %s" % (sw,))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -297,7 +300,8 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
if isim_card.file_exists(EF_ISIM_ADF_map['PCSCF']):
|
if isim_card.file_exists(EF_ISIM_ADF_map['PCSCF']):
|
||||||
res = isim_card.read_pcscf()
|
res = isim_card.read_pcscf()
|
||||||
print("P-CSCF:\n%s" % (len(res) and res or '\tNot available\n',))
|
print("P-CSCF:\n%s" %
|
||||||
|
(len(res) and res or '\tNot available\n',))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("P-CSCF: Can't read file -- " + str(e))
|
print("P-CSCF: Can't read file -- " + str(e))
|
||||||
|
|
||||||
@@ -306,9 +310,11 @@ if __name__ == '__main__':
|
|||||||
if isim_card.file_exists(EF_ISIM_ADF_map['DOMAIN']):
|
if isim_card.file_exists(EF_ISIM_ADF_map['DOMAIN']):
|
||||||
(res, sw) = isim_card.read_domain()
|
(res, sw) = isim_card.read_domain()
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
print("Home Network Domain Name: %s" % (len(res) and res or 'Not available',))
|
print("Home Network Domain Name: %s" %
|
||||||
|
(len(res) and res or 'Not available',))
|
||||||
else:
|
else:
|
||||||
print("Home Network Domain Name: Can't read, response code = %s" % (sw,))
|
print(
|
||||||
|
"Home Network Domain Name: Can't read, response code = %s" % (sw,))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Home Network Domain Name: Can't read file -- " + str(e))
|
print("Home Network Domain Name: Can't read file -- " + str(e))
|
||||||
|
|
||||||
@@ -317,9 +323,11 @@ if __name__ == '__main__':
|
|||||||
if isim_card.file_exists(EF_ISIM_ADF_map['IMPI']):
|
if isim_card.file_exists(EF_ISIM_ADF_map['IMPI']):
|
||||||
(res, sw) = isim_card.read_impi()
|
(res, sw) = isim_card.read_impi()
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
print("IMS private user identity: %s" % (len(res) and res or 'Not available',))
|
print("IMS private user identity: %s" %
|
||||||
|
(len(res) and res or 'Not available',))
|
||||||
else:
|
else:
|
||||||
print("IMS private user identity: Can't read, response code = %s" % (sw,))
|
print(
|
||||||
|
"IMS private user identity: Can't read, response code = %s" % (sw,))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("IMS private user identity: Can't read file -- " + str(e))
|
print("IMS private user identity: Can't read file -- " + str(e))
|
||||||
|
|
||||||
@@ -327,7 +335,8 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
if isim_card.file_exists(EF_ISIM_ADF_map['IMPU']):
|
if isim_card.file_exists(EF_ISIM_ADF_map['IMPU']):
|
||||||
res = isim_card.read_impu()
|
res = isim_card.read_impu()
|
||||||
print("IMS public user identity:\n%s" % (len(res) and res or '\tNot available\n',))
|
print("IMS public user identity:\n%s" %
|
||||||
|
(len(res) and res or '\tNot available\n',))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("IMS public user identity: Can't read file -- " + str(e))
|
print("IMS public user identity: Can't read file -- " + str(e))
|
||||||
|
|
||||||
@@ -335,7 +344,8 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
if isim_card.file_exists(EF_ISIM_ADF_map['UICCIARI']):
|
if isim_card.file_exists(EF_ISIM_ADF_map['UICCIARI']):
|
||||||
res = isim_card.read_iari()
|
res = isim_card.read_iari()
|
||||||
print("UICC IARI:\n%s" % (len(res) and res or '\tNot available\n',))
|
print("UICC IARI:\n%s" %
|
||||||
|
(len(res) and res or '\tNot available\n',))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("UICC IARI: Can't read file -- " + str(e))
|
print("UICC IARI: Can't read file -- " + str(e))
|
||||||
|
|
||||||
|
|||||||
179
pySim-shell.py
179
pySim-shell.py
@@ -61,6 +61,7 @@ import pySim.sysmocom_sja2
|
|||||||
|
|
||||||
from pySim.card_key_provider import CardKeyProviderCsv, card_key_provider_register, card_key_provider_get_field
|
from pySim.card_key_provider import CardKeyProviderCsv, card_key_provider_register, card_key_provider_get_field
|
||||||
|
|
||||||
|
|
||||||
def init_card(sl):
|
def init_card(sl):
|
||||||
"""
|
"""
|
||||||
Detect card in reader and setup card profile and runtime state. This
|
Detect card in reader and setup card profile and runtime state. This
|
||||||
@@ -117,8 +118,10 @@ def init_card(sl):
|
|||||||
|
|
||||||
return rs, card
|
return rs, card
|
||||||
|
|
||||||
|
|
||||||
class PysimApp(cmd2.Cmd):
|
class PysimApp(cmd2.Cmd):
|
||||||
CUSTOM_CATEGORY = 'pySim Commands'
|
CUSTOM_CATEGORY = 'pySim Commands'
|
||||||
|
|
||||||
def __init__(self, card, rs, sl, ch, script=None):
|
def __init__(self, card, rs, sl, ch, script=None):
|
||||||
super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False,
|
super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False,
|
||||||
use_ipython=True, auto_load_commands=False, startup_script=script)
|
use_ipython=True, auto_load_commands=False, startup_script=script)
|
||||||
@@ -137,7 +140,8 @@ class PysimApp(cmd2.Cmd):
|
|||||||
self.add_settable(cmd2.Settable('conserve_write', bool, 'Read and compare before write',
|
self.add_settable(cmd2.Settable('conserve_write', bool, 'Read and compare before write',
|
||||||
onchange_cb=self._onchange_conserve_write))
|
onchange_cb=self._onchange_conserve_write))
|
||||||
self.json_pretty_print = True
|
self.json_pretty_print = True
|
||||||
self.add_settable(cmd2.Settable('json_pretty_print', bool, 'Pretty-Print JSON output'))
|
self.add_settable(cmd2.Settable('json_pretty_print',
|
||||||
|
bool, 'Pretty-Print JSON output'))
|
||||||
self.apdu_trace = False
|
self.apdu_trace = False
|
||||||
self.add_settable(cmd2.Settable('apdu_trace', bool, 'Trace and display APDUs exchanged with card',
|
self.add_settable(cmd2.Settable('apdu_trace', bool, 'Trace and display APDUs exchanged with card',
|
||||||
onchange_cb=self._onchange_apdu_trace))
|
onchange_cb=self._onchange_apdu_trace))
|
||||||
@@ -166,7 +170,8 @@ class PysimApp(cmd2.Cmd):
|
|||||||
# When a card object and a runtime state is present, (re)equip pySim-shell with everything that is
|
# When a card object and a runtime state is present, (re)equip pySim-shell with everything that is
|
||||||
# needed to operate on cards.
|
# needed to operate on cards.
|
||||||
if self.card and self.rs:
|
if self.card and self.rs:
|
||||||
self._onchange_conserve_write('conserve_write', False, self.conserve_write)
|
self._onchange_conserve_write(
|
||||||
|
'conserve_write', False, self.conserve_write)
|
||||||
self._onchange_apdu_trace('apdu_trace', False, self.apdu_trace)
|
self._onchange_apdu_trace('apdu_trace', False, self.apdu_trace)
|
||||||
self.register_command_set(Iso7816Commands())
|
self.register_command_set(Iso7816Commands())
|
||||||
self.register_command_set(PySimCommands())
|
self.register_command_set(PySimCommands())
|
||||||
@@ -211,7 +216,8 @@ class PysimApp(cmd2.Cmd):
|
|||||||
|
|
||||||
def update_prompt(self):
|
def update_prompt(self):
|
||||||
if self.rs:
|
if self.rs:
|
||||||
path_list = self.rs.selected_file.fully_qualified_path(not self.numeric_path)
|
path_list = self.rs.selected_file.fully_qualified_path(
|
||||||
|
not self.numeric_path)
|
||||||
self.prompt = 'pySIM-shell (%s)> ' % ('/'.join(path_list))
|
self.prompt = 'pySIM-shell (%s)> ' % ('/'.join(path_list))
|
||||||
else:
|
else:
|
||||||
self.prompt = 'pySIM-shell (no card)> '
|
self.prompt = 'pySIM-shell (no card)> '
|
||||||
@@ -234,10 +240,12 @@ class PysimApp(cmd2.Cmd):
|
|||||||
class InterceptStderr(list):
|
class InterceptStderr(list):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._stderr_backup = sys.stderr
|
self._stderr_backup = sys.stderr
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self._stringio_stderr = StringIO()
|
self._stringio_stderr = StringIO()
|
||||||
sys.stderr = self._stringio_stderr
|
sys.stderr = self._stringio_stderr
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.stderr = self._stringio_stderr.getvalue().strip()
|
self.stderr = self._stringio_stderr.getvalue().strip()
|
||||||
del self._stringio_stderr
|
del self._stringio_stderr
|
||||||
@@ -305,7 +313,8 @@ class PysimApp(cmd2.Cmd):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
bulk_script_parser = argparse.ArgumentParser()
|
bulk_script_parser = argparse.ArgumentParser()
|
||||||
bulk_script_parser.add_argument('script_path', help="path to the script file")
|
bulk_script_parser.add_argument(
|
||||||
|
'script_path', help="path to the script file")
|
||||||
bulk_script_parser.add_argument('--halt_on_error', help='stop card handling if an exeption occurs',
|
bulk_script_parser.add_argument('--halt_on_error', help='stop card handling if an exeption occurs',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
bulk_script_parser.add_argument('--tries', type=int, default=2,
|
bulk_script_parser.add_argument('--tries', type=int, default=2,
|
||||||
@@ -350,13 +359,14 @@ class PysimApp(cmd2.Cmd):
|
|||||||
if rc == 0:
|
if rc == 0:
|
||||||
success_count = success_count + 1
|
success_count = success_count + 1
|
||||||
self._show_success_sign()
|
self._show_success_sign()
|
||||||
self.poutput("Statistics: success :%i, failure: %i" % (success_count, fail_count))
|
self.poutput("Statistics: success :%i, failure: %i" % (
|
||||||
|
success_count, fail_count))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
fail_count = fail_count + 1
|
fail_count = fail_count + 1
|
||||||
self._show_failure_sign()
|
self._show_failure_sign()
|
||||||
self.poutput("Statistics: success :%i, failure: %i" % (success_count, fail_count))
|
self.poutput("Statistics: success :%i, failure: %i" % (
|
||||||
|
success_count, fail_count))
|
||||||
|
|
||||||
# Depending on success or failure, the card goes either in the "error" bin or in the
|
# Depending on success or failure, the card goes either in the "error" bin or in the
|
||||||
# "done" bin.
|
# "done" bin.
|
||||||
@@ -391,7 +401,8 @@ class PysimApp(cmd2.Cmd):
|
|||||||
self.poutput("")
|
self.poutput("")
|
||||||
fail_count = fail_count + 1
|
fail_count = fail_count + 1
|
||||||
self._show_failure_sign()
|
self._show_failure_sign()
|
||||||
self.poutput("Statistics: success :%i, failure: %i" % (success_count, fail_count))
|
self.poutput("Statistics: success :%i, failure: %i" %
|
||||||
|
(success_count, fail_count))
|
||||||
|
|
||||||
first = False
|
first = False
|
||||||
|
|
||||||
@@ -404,16 +415,21 @@ class PysimApp(cmd2.Cmd):
|
|||||||
"""Echo (print) a string on the console"""
|
"""Echo (print) a string on the console"""
|
||||||
self.poutput(opts.string)
|
self.poutput(opts.string)
|
||||||
|
|
||||||
|
|
||||||
@with_default_category('pySim Commands')
|
@with_default_category('pySim Commands')
|
||||||
class PySimCommands(CommandSet):
|
class PySimCommands(CommandSet):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
dir_parser = argparse.ArgumentParser()
|
dir_parser = argparse.ArgumentParser()
|
||||||
dir_parser.add_argument('--fids', help='Show file identifiers', action='store_true')
|
dir_parser.add_argument(
|
||||||
dir_parser.add_argument('--names', help='Show file names', action='store_true')
|
'--fids', help='Show file identifiers', action='store_true')
|
||||||
dir_parser.add_argument('--apps', help='Show applications', action='store_true')
|
dir_parser.add_argument(
|
||||||
dir_parser.add_argument('--all', help='Show all selectable identifiers and names', action='store_true')
|
'--names', help='Show file names', action='store_true')
|
||||||
|
dir_parser.add_argument(
|
||||||
|
'--apps', help='Show applications', action='store_true')
|
||||||
|
dir_parser.add_argument(
|
||||||
|
'--all', help='Show all selectable identifiers and names', action='store_true')
|
||||||
|
|
||||||
@cmd2.with_argparser(dir_parser)
|
@cmd2.with_argparser(dir_parser)
|
||||||
def do_dir(self, opts):
|
def do_dir(self, opts):
|
||||||
@@ -430,8 +446,10 @@ class PySimCommands(CommandSet):
|
|||||||
flags += ['ANAMES', 'AIDS']
|
flags += ['ANAMES', 'AIDS']
|
||||||
else:
|
else:
|
||||||
flags = ['PARENT', 'SELF', 'FNAMES', 'ANAMES']
|
flags = ['PARENT', 'SELF', 'FNAMES', 'ANAMES']
|
||||||
selectables = list(self._cmd.rs.selected_file.get_selectable_names(flags = flags))
|
selectables = list(
|
||||||
directory_str = tabulate_str_list(selectables, width = 79, hspace = 2, lspace = 1, align_left = True)
|
self._cmd.rs.selected_file.get_selectable_names(flags=flags))
|
||||||
|
directory_str = tabulate_str_list(
|
||||||
|
selectables, width=79, hspace=2, lspace=1, align_left=True)
|
||||||
path_list = self._cmd.rs.selected_file.fully_qualified_path(True)
|
path_list = self._cmd.rs.selected_file.fully_qualified_path(True)
|
||||||
self._cmd.poutput('/'.join(path_list))
|
self._cmd.poutput('/'.join(path_list))
|
||||||
path_list = self._cmd.rs.selected_file.fully_qualified_path(False)
|
path_list = self._cmd.rs.selected_file.fully_qualified_path(False)
|
||||||
@@ -441,7 +459,8 @@ class PySimCommands(CommandSet):
|
|||||||
|
|
||||||
def walk(self, indent=0, action=None, context=None):
|
def walk(self, indent=0, action=None, context=None):
|
||||||
"""Recursively walk through the file system, starting at the currently selected DF"""
|
"""Recursively walk through the file system, starting at the currently selected DF"""
|
||||||
files = self._cmd.rs.selected_file.get_selectables(flags = ['FNAMES', 'ANAMES'])
|
files = self._cmd.rs.selected_file.get_selectables(
|
||||||
|
flags=['FNAMES', 'ANAMES'])
|
||||||
for f in files:
|
for f in files:
|
||||||
if not action:
|
if not action:
|
||||||
output_str = " " * indent + str(f) + (" " * 250)
|
output_str = " " * indent + str(f) + (" " * 250)
|
||||||
@@ -461,7 +480,8 @@ class PySimCommands(CommandSet):
|
|||||||
skip_df = True
|
skip_df = True
|
||||||
df = self._cmd.rs.selected_file
|
df = self._cmd.rs.selected_file
|
||||||
df_path_list = df.fully_qualified_path(True)
|
df_path_list = df.fully_qualified_path(True)
|
||||||
df_skip_reason_str = '/'.join(df_path_list) + "/" + str(f) + ", " + str(e)
|
df_skip_reason_str = '/'.join(df_path_list) + \
|
||||||
|
"/" + str(f) + ", " + str(e)
|
||||||
if context:
|
if context:
|
||||||
context['DF_SKIP'] += 1
|
context['DF_SKIP'] += 1
|
||||||
context['DF_SKIP_REASON'].append(df_skip_reason_str)
|
context['DF_SKIP_REASON'].append(df_skip_reason_str)
|
||||||
@@ -492,7 +512,8 @@ class PySimCommands(CommandSet):
|
|||||||
df = self._cmd.rs.selected_file
|
df = self._cmd.rs.selected_file
|
||||||
|
|
||||||
if not isinstance(df, CardDF):
|
if not isinstance(df, CardDF):
|
||||||
raise RuntimeError("currently selected file %s is not a DF or ADF" % str(df))
|
raise RuntimeError(
|
||||||
|
"currently selected file %s is not a DF or ADF" % str(df))
|
||||||
|
|
||||||
df_path_list = df.fully_qualified_path(True)
|
df_path_list = df.fully_qualified_path(True)
|
||||||
df_path_list_fid = df.fully_qualified_path(False)
|
df_path_list_fid = df.fully_qualified_path(False)
|
||||||
@@ -500,10 +521,12 @@ class PySimCommands(CommandSet):
|
|||||||
file_str = '/'.join(df_path_list) + "/" + str(filename)
|
file_str = '/'.join(df_path_list) + "/" + str(filename)
|
||||||
self._cmd.poutput(boxed_heading_str(file_str))
|
self._cmd.poutput(boxed_heading_str(file_str))
|
||||||
|
|
||||||
self._cmd.poutput("# directory: %s (%s)" % ('/'.join(df_path_list), '/'.join(df_path_list_fid)))
|
self._cmd.poutput("# directory: %s (%s)" %
|
||||||
|
('/'.join(df_path_list), '/'.join(df_path_list_fid)))
|
||||||
try:
|
try:
|
||||||
fcp_dec = self._cmd.rs.select(filename, self._cmd)
|
fcp_dec = self._cmd.rs.select(filename, self._cmd)
|
||||||
self._cmd.poutput("# file: %s (%s)" % (self._cmd.rs.selected_file.name, self._cmd.rs.selected_file.fid))
|
self._cmd.poutput("# file: %s (%s)" % (
|
||||||
|
self._cmd.rs.selected_file.name, self._cmd.rs.selected_file.fid))
|
||||||
|
|
||||||
fd = fcp_dec['file_descriptor']
|
fd = fcp_dec['file_descriptor']
|
||||||
structure = fd['structure']
|
structure = fd['structure']
|
||||||
@@ -522,7 +545,8 @@ class PySimCommands(CommandSet):
|
|||||||
num_of_rec = fd['num_of_rec']
|
num_of_rec = fd['num_of_rec']
|
||||||
for r in range(1, num_of_rec + 1):
|
for r in range(1, num_of_rec + 1):
|
||||||
result = self._cmd.rs.read_record(r)
|
result = self._cmd.rs.read_record(r)
|
||||||
self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
|
self._cmd.poutput("update_record %d %s" %
|
||||||
|
(r, str(result[0])))
|
||||||
# When the select response does not return the number of records, read until we hit the
|
# When the select response does not return the number of records, read until we hit the
|
||||||
# first record that cannot be read.
|
# first record that cannot be read.
|
||||||
else:
|
else:
|
||||||
@@ -537,7 +561,8 @@ class PySimCommands(CommandSet):
|
|||||||
# Some other problem occurred
|
# Some other problem occurred
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
|
self._cmd.poutput("update_record %d %s" %
|
||||||
|
(r, str(result[0])))
|
||||||
r = r + 1
|
r = r + 1
|
||||||
elif structure == 'ber_tlv':
|
elif structure == 'ber_tlv':
|
||||||
tags = self._cmd.rs.retrieve_tags()
|
tags = self._cmd.rs.retrieve_tags()
|
||||||
@@ -546,9 +571,11 @@ class PySimCommands(CommandSet):
|
|||||||
(tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
|
(tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
|
||||||
self._cmd.poutput("set_data 0x%02x %s" % (t, b2h(val)))
|
self._cmd.poutput("set_data 0x%02x %s" % (t, b2h(val)))
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Unsupported structure "%s" of file "%s"' % (structure, filename))
|
raise RuntimeError(
|
||||||
|
'Unsupported structure "%s" of file "%s"' % (structure, filename))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
bad_file_str = '/'.join(df_path_list) + "/" + str(filename) + ", " + str(e)
|
bad_file_str = '/'.join(df_path_list) + \
|
||||||
|
"/" + str(filename) + ", " + str(e)
|
||||||
self._cmd.poutput("# bad file: %s" % bad_file_str)
|
self._cmd.poutput("# bad file: %s" % bad_file_str)
|
||||||
context['ERR'] += 1
|
context['ERR'] += 1
|
||||||
context['BAD'].append(bad_file_str)
|
context['BAD'].append(bad_file_str)
|
||||||
@@ -562,12 +589,14 @@ class PySimCommands(CommandSet):
|
|||||||
self._cmd.poutput("#")
|
self._cmd.poutput("#")
|
||||||
|
|
||||||
export_parser = argparse.ArgumentParser()
|
export_parser = argparse.ArgumentParser()
|
||||||
export_parser.add_argument('--filename', type=str, default=None, help='only export specific file')
|
export_parser.add_argument(
|
||||||
|
'--filename', type=str, default=None, help='only export specific file')
|
||||||
|
|
||||||
@cmd2.with_argparser(export_parser)
|
@cmd2.with_argparser(export_parser)
|
||||||
def do_export(self, opts):
|
def do_export(self, opts):
|
||||||
"""Export files to script that can be imported back later"""
|
"""Export files to script that can be imported back later"""
|
||||||
context = {'ERR':0, 'COUNT':0, 'BAD':[], 'DF_SKIP':0, 'DF_SKIP_REASON':[]}
|
context = {'ERR': 0, 'COUNT': 0, 'BAD': [],
|
||||||
|
'DF_SKIP': 0, 'DF_SKIP_REASON': []}
|
||||||
if opts.filename:
|
if opts.filename:
|
||||||
self.export(opts.filename, context)
|
self.export(opts.filename, context)
|
||||||
else:
|
else:
|
||||||
@@ -580,16 +609,20 @@ class PySimCommands(CommandSet):
|
|||||||
for b in context['BAD']:
|
for b in context['BAD']:
|
||||||
self._cmd.poutput("# " + b)
|
self._cmd.poutput("# " + b)
|
||||||
|
|
||||||
self._cmd.poutput("# skipped dedicated files(s): %u" % context['DF_SKIP'])
|
self._cmd.poutput("# skipped dedicated files(s): %u" %
|
||||||
|
context['DF_SKIP'])
|
||||||
for b in context['DF_SKIP_REASON']:
|
for b in context['DF_SKIP_REASON']:
|
||||||
self._cmd.poutput("# " + b)
|
self._cmd.poutput("# " + b)
|
||||||
|
|
||||||
if context['ERR'] and context['DF_SKIP']:
|
if context['ERR'] and context['DF_SKIP']:
|
||||||
raise RuntimeError("unable to export %i elementary file(s) and %i dedicated file(s)" % (context['ERR'], context['DF_SKIP']))
|
raise RuntimeError("unable to export %i elementary file(s) and %i dedicated file(s)" % (
|
||||||
|
context['ERR'], context['DF_SKIP']))
|
||||||
elif context['ERR']:
|
elif context['ERR']:
|
||||||
raise RuntimeError("unable to export %i elementary file(s)" % context['ERR'])
|
raise RuntimeError(
|
||||||
|
"unable to export %i elementary file(s)" % context['ERR'])
|
||||||
elif context['DF_SKIP']:
|
elif context['DF_SKIP']:
|
||||||
raise RuntimeError("unable to export %i dedicated files(s)" % context['ERR'])
|
raise RuntimeError(
|
||||||
|
"unable to export %i dedicated files(s)" % context['ERR'])
|
||||||
|
|
||||||
def do_reset(self, opts):
|
def do_reset(self, opts):
|
||||||
"""Reset the Card."""
|
"""Reset the Card."""
|
||||||
@@ -612,18 +645,22 @@ class PySimCommands(CommandSet):
|
|||||||
pin_adm = sanitize_pin_adm(arg)
|
pin_adm = sanitize_pin_adm(arg)
|
||||||
else:
|
else:
|
||||||
# try to find an ADM-PIN if none is specified
|
# try to find an ADM-PIN if none is specified
|
||||||
result = card_key_provider_get_field('ADM1', key='ICCID', value=self._cmd.iccid)
|
result = card_key_provider_get_field(
|
||||||
|
'ADM1', key='ICCID', value=self._cmd.iccid)
|
||||||
pin_adm = sanitize_pin_adm(result)
|
pin_adm = sanitize_pin_adm(result)
|
||||||
if pin_adm:
|
if pin_adm:
|
||||||
self._cmd.poutput("found ADM-PIN '%s' for ICCID '%s'" % (result, self._cmd.iccid))
|
self._cmd.poutput(
|
||||||
|
"found ADM-PIN '%s' for ICCID '%s'" % (result, self._cmd.iccid))
|
||||||
else:
|
else:
|
||||||
raise ValueError("cannot find ADM-PIN for ICCID '%s'" % (self._cmd.iccid))
|
raise ValueError(
|
||||||
|
"cannot find ADM-PIN for ICCID '%s'" % (self._cmd.iccid))
|
||||||
|
|
||||||
if pin_adm:
|
if pin_adm:
|
||||||
self._cmd.card.verify_adm(h2b(pin_adm))
|
self._cmd.card.verify_adm(h2b(pin_adm))
|
||||||
else:
|
else:
|
||||||
raise ValueError("error: cannot authenticate, no adm-pin!")
|
raise ValueError("error: cannot authenticate, no adm-pin!")
|
||||||
|
|
||||||
|
|
||||||
@with_default_category('ISO7816 Commands')
|
@with_default_category('ISO7816 Commands')
|
||||||
class Iso7816Commands(CommandSet):
|
class Iso7816Commands(CommandSet):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -633,8 +670,10 @@ class Iso7816Commands(CommandSet):
|
|||||||
"""SELECT a File (ADF/DF/EF)"""
|
"""SELECT a File (ADF/DF/EF)"""
|
||||||
if len(opts.arg_list) == 0:
|
if len(opts.arg_list) == 0:
|
||||||
path_list = self._cmd.rs.selected_file.fully_qualified_path(True)
|
path_list = self._cmd.rs.selected_file.fully_qualified_path(True)
|
||||||
path_list_fid = self._cmd.rs.selected_file.fully_qualified_path(False)
|
path_list_fid = self._cmd.rs.selected_file.fully_qualified_path(
|
||||||
self._cmd.poutput("currently selected file: " + '/'.join(path_list) + " (" + '/'.join(path_list_fid) + ")")
|
False)
|
||||||
|
self._cmd.poutput("currently selected file: " +
|
||||||
|
'/'.join(path_list) + " (" + '/'.join(path_list_fid) + ")")
|
||||||
return
|
return
|
||||||
|
|
||||||
path = opts.arg_list[0]
|
path = opts.arg_list[0]
|
||||||
@@ -654,17 +693,22 @@ class Iso7816Commands(CommandSet):
|
|||||||
if str(code).upper() not in auto:
|
if str(code).upper() not in auto:
|
||||||
return sanitize_pin_adm(code)
|
return sanitize_pin_adm(code)
|
||||||
|
|
||||||
result = card_key_provider_get_field(str(code), key='ICCID', value=self._cmd.iccid)
|
result = card_key_provider_get_field(
|
||||||
|
str(code), key='ICCID', value=self._cmd.iccid)
|
||||||
result = sanitize_pin_adm(result)
|
result = sanitize_pin_adm(result)
|
||||||
if result:
|
if result:
|
||||||
self._cmd.poutput("found %s '%s' for ICCID '%s'" % (code.upper(), result, self._cmd.iccid))
|
self._cmd.poutput("found %s '%s' for ICCID '%s'" %
|
||||||
|
(code.upper(), result, self._cmd.iccid))
|
||||||
else:
|
else:
|
||||||
self._cmd.poutput("cannot find %s for ICCID '%s'" % (code.upper(), self._cmd.iccid))
|
self._cmd.poutput("cannot find %s for ICCID '%s'" %
|
||||||
|
(code.upper(), self._cmd.iccid))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
verify_chv_parser = argparse.ArgumentParser()
|
verify_chv_parser = argparse.ArgumentParser()
|
||||||
verify_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
verify_chv_parser.add_argument(
|
||||||
verify_chv_parser.add_argument('pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
'--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
||||||
|
verify_chv_parser.add_argument(
|
||||||
|
'pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
||||||
|
|
||||||
@cmd2.with_argparser(verify_chv_parser)
|
@cmd2.with_argparser(verify_chv_parser)
|
||||||
def do_verify_chv(self, opts):
|
def do_verify_chv(self, opts):
|
||||||
@@ -674,34 +718,44 @@ class Iso7816Commands(CommandSet):
|
|||||||
self._cmd.poutput("CHV verification successful")
|
self._cmd.poutput("CHV verification successful")
|
||||||
|
|
||||||
unblock_chv_parser = argparse.ArgumentParser()
|
unblock_chv_parser = argparse.ArgumentParser()
|
||||||
unblock_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PUK Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
unblock_chv_parser.add_argument(
|
||||||
unblock_chv_parser.add_argument('puk_code', type=str, help='PUK code digits \"PUK1\" or \"PUK2\" to get PUK code from external data source')
|
'--pin-nr', type=int, default=1, help='PUK Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
||||||
unblock_chv_parser.add_argument('new_pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
unblock_chv_parser.add_argument(
|
||||||
|
'puk_code', type=str, help='PUK code digits \"PUK1\" or \"PUK2\" to get PUK code from external data source')
|
||||||
|
unblock_chv_parser.add_argument(
|
||||||
|
'new_pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
||||||
|
|
||||||
@cmd2.with_argparser(unblock_chv_parser)
|
@cmd2.with_argparser(unblock_chv_parser)
|
||||||
def do_unblock_chv(self, opts):
|
def do_unblock_chv(self, opts):
|
||||||
"""Unblock PIN code using specified PUK code"""
|
"""Unblock PIN code using specified PUK code"""
|
||||||
new_pin = self.get_code(opts.new_pin_code)
|
new_pin = self.get_code(opts.new_pin_code)
|
||||||
puk = self.get_code(opts.puk_code)
|
puk = self.get_code(opts.puk_code)
|
||||||
(data, sw) = self._cmd.card._scc.unblock_chv(opts.pin_nr, h2b(puk), h2b(new_pin))
|
(data, sw) = self._cmd.card._scc.unblock_chv(
|
||||||
|
opts.pin_nr, h2b(puk), h2b(new_pin))
|
||||||
self._cmd.poutput("CHV unblock successful")
|
self._cmd.poutput("CHV unblock successful")
|
||||||
|
|
||||||
change_chv_parser = argparse.ArgumentParser()
|
change_chv_parser = argparse.ArgumentParser()
|
||||||
change_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PUK Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
change_chv_parser.add_argument(
|
||||||
change_chv_parser.add_argument('pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
'--pin-nr', type=int, default=1, help='PUK Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
||||||
change_chv_parser.add_argument('new_pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
change_chv_parser.add_argument(
|
||||||
|
'pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
||||||
|
change_chv_parser.add_argument(
|
||||||
|
'new_pin_code', type=str, help='PIN code digits \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
||||||
|
|
||||||
@cmd2.with_argparser(change_chv_parser)
|
@cmd2.with_argparser(change_chv_parser)
|
||||||
def do_change_chv(self, opts):
|
def do_change_chv(self, opts):
|
||||||
"""Change PIN code to a new PIN code"""
|
"""Change PIN code to a new PIN code"""
|
||||||
new_pin = self.get_code(opts.new_pin_code)
|
new_pin = self.get_code(opts.new_pin_code)
|
||||||
pin = self.get_code(opts.pin_code)
|
pin = self.get_code(opts.pin_code)
|
||||||
(data, sw) = self._cmd.card._scc.change_chv(opts.pin_nr, h2b(pin), h2b(new_pin))
|
(data, sw) = self._cmd.card._scc.change_chv(
|
||||||
|
opts.pin_nr, h2b(pin), h2b(new_pin))
|
||||||
self._cmd.poutput("CHV change successful")
|
self._cmd.poutput("CHV change successful")
|
||||||
|
|
||||||
disable_chv_parser = argparse.ArgumentParser()
|
disable_chv_parser = argparse.ArgumentParser()
|
||||||
disable_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
disable_chv_parser.add_argument(
|
||||||
disable_chv_parser.add_argument('pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
'--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
||||||
|
disable_chv_parser.add_argument(
|
||||||
|
'pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
||||||
|
|
||||||
@cmd2.with_argparser(disable_chv_parser)
|
@cmd2.with_argparser(disable_chv_parser)
|
||||||
def do_disable_chv(self, opts):
|
def do_disable_chv(self, opts):
|
||||||
@@ -711,8 +765,10 @@ class Iso7816Commands(CommandSet):
|
|||||||
self._cmd.poutput("CHV disable successful")
|
self._cmd.poutput("CHV disable successful")
|
||||||
|
|
||||||
enable_chv_parser = argparse.ArgumentParser()
|
enable_chv_parser = argparse.ArgumentParser()
|
||||||
enable_chv_parser.add_argument('--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
enable_chv_parser.add_argument(
|
||||||
enable_chv_parser.add_argument('pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
'--pin-nr', type=int, default=1, help='PIN Number, 1=PIN1, 2=PIN2 or custom value (decimal)')
|
||||||
|
enable_chv_parser.add_argument(
|
||||||
|
'pin_code', type=str, help='PIN code digits, \"PIN1\" or \"PIN2\" to get PIN code from external data source')
|
||||||
|
|
||||||
@cmd2.with_argparser(enable_chv_parser)
|
@cmd2.with_argparser(enable_chv_parser)
|
||||||
def do_enable_chv(self, opts):
|
def do_enable_chv(self, opts):
|
||||||
@@ -736,20 +792,24 @@ class Iso7816Commands(CommandSet):
|
|||||||
return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
|
return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
|
||||||
|
|
||||||
open_chan_parser = argparse.ArgumentParser()
|
open_chan_parser = argparse.ArgumentParser()
|
||||||
open_chan_parser.add_argument('chan_nr', type=int, default=0, help='Channel Number')
|
open_chan_parser.add_argument(
|
||||||
|
'chan_nr', type=int, default=0, help='Channel Number')
|
||||||
|
|
||||||
@cmd2.with_argparser(open_chan_parser)
|
@cmd2.with_argparser(open_chan_parser)
|
||||||
def do_open_channel(self, opts):
|
def do_open_channel(self, opts):
|
||||||
"""Open a logical channel."""
|
"""Open a logical channel."""
|
||||||
(data, sw) = self._cmd.card._scc.manage_channel(mode='open', lchan_nr=opts.chan_nr)
|
(data, sw) = self._cmd.card._scc.manage_channel(
|
||||||
|
mode='open', lchan_nr=opts.chan_nr)
|
||||||
|
|
||||||
close_chan_parser = argparse.ArgumentParser()
|
close_chan_parser = argparse.ArgumentParser()
|
||||||
close_chan_parser.add_argument('chan_nr', type=int, default=0, help='Channel Number')
|
close_chan_parser.add_argument(
|
||||||
|
'chan_nr', type=int, default=0, help='Channel Number')
|
||||||
|
|
||||||
@cmd2.with_argparser(close_chan_parser)
|
@cmd2.with_argparser(close_chan_parser)
|
||||||
def do_close_channel(self, opts):
|
def do_close_channel(self, opts):
|
||||||
"""Close a logical channel."""
|
"""Close a logical channel."""
|
||||||
(data, sw) = self._cmd.card._scc.manage_channel(mode='close', lchan_nr=opts.chan_nr)
|
(data, sw) = self._cmd.card._scc.manage_channel(
|
||||||
|
mode='close', lchan_nr=opts.chan_nr)
|
||||||
|
|
||||||
def do_status(self, opts):
|
def do_status(self, opts):
|
||||||
"""Perform the STATUS command."""
|
"""Perform the STATUS command."""
|
||||||
@@ -768,7 +828,8 @@ class Iso7816Commands(CommandSet):
|
|||||||
"""Perform the SUSPEND UICC command. Only supported on some UICC."""
|
"""Perform the SUSPEND UICC command. Only supported on some UICC."""
|
||||||
(duration, token, sw) = self._cmd.card._scc.suspend_uicc(min_len_secs=opts.min_duration_secs,
|
(duration, token, sw) = self._cmd.card._scc.suspend_uicc(min_len_secs=opts.min_duration_secs,
|
||||||
max_len_secs=opts.max_duration_secs)
|
max_len_secs=opts.max_duration_secs)
|
||||||
self._cmd.poutput('Negotiated Duration: %u secs, Token: %s, SW: %s' % (duration, token, sw))
|
self._cmd.poutput(
|
||||||
|
'Negotiated Duration: %u secs, Token: %s, SW: %s' % (duration, token, sw))
|
||||||
|
|
||||||
|
|
||||||
option_parser = argparse.ArgumentParser(prog='pySim-shell', description='interactive SIM card shell',
|
option_parser = argparse.ArgumentParser(prog='pySim-shell', description='interactive SIM card shell',
|
||||||
@@ -778,7 +839,8 @@ argparse_add_reader_args(option_parser)
|
|||||||
global_group = option_parser.add_argument_group('General Options')
|
global_group = option_parser.add_argument_group('General Options')
|
||||||
global_group.add_argument('--script', metavar='PATH', default=None,
|
global_group.add_argument('--script', metavar='PATH', default=None,
|
||||||
help='script with pySim-shell commands to be executed automatically at start-up')
|
help='script with pySim-shell commands to be executed automatically at start-up')
|
||||||
global_group.add_argument('--csv', metavar='FILE', default=None, help='Read card data from CSV file')
|
global_group.add_argument('--csv', metavar='FILE',
|
||||||
|
default=None, help='Read card data from CSV file')
|
||||||
global_group.add_argument("--card_handler", dest="card_handler_config", metavar="FILE",
|
global_group.add_argument("--card_handler", dest="card_handler_config", metavar="FILE",
|
||||||
help="Use automatic card handling machine")
|
help="Use automatic card handling machine")
|
||||||
|
|
||||||
@@ -834,7 +896,8 @@ if __name__ == '__main__':
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print("---------------------8<---------------------")
|
print("---------------------8<---------------------")
|
||||||
print("(you may still try to recover from this manually by using the 'equip' command.)")
|
print("(you may still try to recover from this manually by using the 'equip' command.)")
|
||||||
print(" it should also be noted that some readers may behave strangely when no card")
|
print(
|
||||||
|
" it should also be noted that some readers may behave strangely when no card")
|
||||||
print(" is inserted.)")
|
print(" is inserted.)")
|
||||||
print("")
|
print("")
|
||||||
app = PysimApp(None, None, sl, ch, opts.script)
|
app = PysimApp(None, None, sl, ch, opts.script)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -34,26 +34,32 @@ from pySim.tlv import *
|
|||||||
|
|
||||||
# various BER-TLV encoded Data Objects (DOs)
|
# various BER-TLV encoded Data Objects (DOs)
|
||||||
|
|
||||||
|
|
||||||
class AidRefDO(BER_TLV_IE, tag=0x4f):
|
class AidRefDO(BER_TLV_IE, tag=0x4f):
|
||||||
# SEID v1.1 Table 6-3
|
# SEID v1.1 Table 6-3
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
|
|
||||||
class AidRefEmptyDO(BER_TLV_IE, tag=0xc0):
|
class AidRefEmptyDO(BER_TLV_IE, tag=0xc0):
|
||||||
# SEID v1.1 Table 6-3
|
# SEID v1.1 Table 6-3
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DevAppIdRefDO(BER_TLV_IE, tag=0xc1):
|
class DevAppIdRefDO(BER_TLV_IE, tag=0xc1):
|
||||||
# SEID v1.1 Table 6-4
|
# SEID v1.1 Table 6-4
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
|
|
||||||
class PkgRefDO(BER_TLV_IE, tag=0xca):
|
class PkgRefDO(BER_TLV_IE, tag=0xca):
|
||||||
# Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc
|
# Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc
|
||||||
_construct = Struct('package_name_string'/GreedyString("ascii"))
|
_construct = Struct('package_name_string'/GreedyString("ascii"))
|
||||||
|
|
||||||
|
|
||||||
class RefDO(BER_TLV_IE, tag=0xe1, nested=[AidRefDO, AidRefEmptyDO, DevAppIdRefDO, PkgRefDO]):
|
class RefDO(BER_TLV_IE, tag=0xe1, nested=[AidRefDO, AidRefEmptyDO, DevAppIdRefDO, PkgRefDO]):
|
||||||
# SEID v1.1 Table 6-5
|
# SEID v1.1 Table 6-5
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ApduArDO(BER_TLV_IE, tag=0xd0):
|
class ApduArDO(BER_TLV_IE, tag=0xd0):
|
||||||
# SEID v1.1 Table 6-8
|
# SEID v1.1 Table 6-8
|
||||||
def _from_bytes(self, do: bytes):
|
def _from_bytes(self, do: bytes):
|
||||||
@@ -76,6 +82,7 @@ class ApduArDO(BER_TLV_IE, tag=0xd0):
|
|||||||
'mask': b2h(do[offset+4:offset+8])}
|
'mask': b2h(do[offset+4:offset+8])}
|
||||||
self.decoded = res
|
self.decoded = res
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _to_bytes(self):
|
def _to_bytes(self):
|
||||||
if 'generic_access_rule' in self.decoded:
|
if 'generic_access_rule' in self.decoded:
|
||||||
if self.decoded['generic_access_rule'] == 'never':
|
if self.decoded['generic_access_rule'] == 'never':
|
||||||
@@ -99,94 +106,118 @@ class ApduArDO(BER_TLV_IE, tag=0xd0):
|
|||||||
res += header_b + mask_b
|
res += header_b + mask_b
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class NfcArDO(BER_TLV_IE, tag=0xd1):
|
class NfcArDO(BER_TLV_IE, tag=0xd1):
|
||||||
# SEID v1.1 Table 6-9
|
# SEID v1.1 Table 6-9
|
||||||
_construct = Struct('nfc_event_access_rule'/Enum(Int8ub, never=0, always=1))
|
_construct = Struct('nfc_event_access_rule' /
|
||||||
|
Enum(Int8ub, never=0, always=1))
|
||||||
|
|
||||||
|
|
||||||
class PermArDO(BER_TLV_IE, tag=0xdb):
|
class PermArDO(BER_TLV_IE, tag=0xdb):
|
||||||
# Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc
|
# Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc
|
||||||
_construct = Struct('permissions'/HexAdapter(Bytes(8)))
|
_construct = Struct('permissions'/HexAdapter(Bytes(8)))
|
||||||
|
|
||||||
|
|
||||||
class ArDO(BER_TLV_IE, tag=0xe3, nested=[ApduArDO, NfcArDO, PermArDO]):
|
class ArDO(BER_TLV_IE, tag=0xe3, nested=[ApduArDO, NfcArDO, PermArDO]):
|
||||||
# SEID v1.1 Table 6-7
|
# SEID v1.1 Table 6-7
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RefArDO(BER_TLV_IE, tag=0xe2, nested=[RefDO, ArDO]):
|
class RefArDO(BER_TLV_IE, tag=0xe2, nested=[RefDO, ArDO]):
|
||||||
# SEID v1.1 Table 6-6
|
# SEID v1.1 Table 6-6
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResponseAllRefArDO(BER_TLV_IE, tag=0xff40, nested=[RefArDO]):
|
class ResponseAllRefArDO(BER_TLV_IE, tag=0xff40, nested=[RefArDO]):
|
||||||
# SEID v1.1 Table 4-2
|
# SEID v1.1 Table 4-2
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResponseArDO(BER_TLV_IE, tag=0xff50, nested=[ArDO]):
|
class ResponseArDO(BER_TLV_IE, tag=0xff50, nested=[ArDO]):
|
||||||
# SEID v1.1 Table 4-3
|
# SEID v1.1 Table 4-3
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResponseRefreshTagDO(BER_TLV_IE, tag=0xdf20):
|
class ResponseRefreshTagDO(BER_TLV_IE, tag=0xdf20):
|
||||||
# SEID v1.1 Table 4-4
|
# SEID v1.1 Table 4-4
|
||||||
_construct = Struct('refresh_tag'/HexAdapter(Bytes(8)))
|
_construct = Struct('refresh_tag'/HexAdapter(Bytes(8)))
|
||||||
|
|
||||||
|
|
||||||
class DeviceInterfaceVersionDO(BER_TLV_IE, tag=0xe6):
|
class DeviceInterfaceVersionDO(BER_TLV_IE, tag=0xe6):
|
||||||
# SEID v1.1 Table 6-12
|
# SEID v1.1 Table 6-12
|
||||||
_construct = Struct('major'/Int8ub, 'minor'/Int8ub, 'patch'/Int8ub)
|
_construct = Struct('major'/Int8ub, 'minor'/Int8ub, 'patch'/Int8ub)
|
||||||
|
|
||||||
|
|
||||||
class DeviceConfigDO(BER_TLV_IE, tag=0xe4, nested=[DeviceInterfaceVersionDO]):
|
class DeviceConfigDO(BER_TLV_IE, tag=0xe4, nested=[DeviceInterfaceVersionDO]):
|
||||||
# SEID v1.1 Table 6-10
|
# SEID v1.1 Table 6-10
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResponseDeviceConfigDO(BER_TLV_IE, tag=0xff7f, nested=[DeviceConfigDO]):
|
class ResponseDeviceConfigDO(BER_TLV_IE, tag=0xff7f, nested=[DeviceConfigDO]):
|
||||||
# SEID v1.1 Table 5-14
|
# SEID v1.1 Table 5-14
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AramConfigDO(BER_TLV_IE, tag=0xe5, nested=[DeviceInterfaceVersionDO]):
|
class AramConfigDO(BER_TLV_IE, tag=0xe5, nested=[DeviceInterfaceVersionDO]):
|
||||||
# SEID v1.1 Table 6-11
|
# SEID v1.1 Table 6-11
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResponseAramConfigDO(BER_TLV_IE, tag=0xdf21, nested=[AramConfigDO]):
|
class ResponseAramConfigDO(BER_TLV_IE, tag=0xdf21, nested=[AramConfigDO]):
|
||||||
# SEID v1.1 Table 4-5
|
# SEID v1.1 Table 4-5
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandStoreRefArDO(BER_TLV_IE, tag=0xf0, nested=[RefArDO]):
|
class CommandStoreRefArDO(BER_TLV_IE, tag=0xf0, nested=[RefArDO]):
|
||||||
# SEID v1.1 Table 5-2
|
# SEID v1.1 Table 5-2
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandDelete(BER_TLV_IE, tag=0xf1, nested=[AidRefDO, AidRefEmptyDO, RefDO, RefArDO]):
|
class CommandDelete(BER_TLV_IE, tag=0xf1, nested=[AidRefDO, AidRefEmptyDO, RefDO, RefArDO]):
|
||||||
# SEID v1.1 Table 5-4
|
# SEID v1.1 Table 5-4
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandUpdateRefreshTagDO(BER_TLV_IE, tag=0xf2):
|
class CommandUpdateRefreshTagDO(BER_TLV_IE, tag=0xf2):
|
||||||
# SEID V1.1 Table 5-6
|
# SEID V1.1 Table 5-6
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandRegisterClientAidsDO(BER_TLV_IE, tag=0xf7, nested=[AidRefDO, AidRefEmptyDO]):
|
class CommandRegisterClientAidsDO(BER_TLV_IE, tag=0xf7, nested=[AidRefDO, AidRefEmptyDO]):
|
||||||
# SEID v1.1 Table 5-7
|
# SEID v1.1 Table 5-7
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandGet(BER_TLV_IE, tag=0xf3, nested=[AidRefDO, AidRefEmptyDO]):
|
class CommandGet(BER_TLV_IE, tag=0xf3, nested=[AidRefDO, AidRefEmptyDO]):
|
||||||
# SEID v1.1 Table 5-8
|
# SEID v1.1 Table 5-8
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandGetAll(BER_TLV_IE, tag=0xf4):
|
class CommandGetAll(BER_TLV_IE, tag=0xf4):
|
||||||
# SEID v1.1 Table 5-9
|
# SEID v1.1 Table 5-9
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandGetClientAidsDO(BER_TLV_IE, tag=0xf6):
|
class CommandGetClientAidsDO(BER_TLV_IE, tag=0xf6):
|
||||||
# SEID v1.1 Table 5-10
|
# SEID v1.1 Table 5-10
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandGetNext(BER_TLV_IE, tag=0xf5):
|
class CommandGetNext(BER_TLV_IE, tag=0xf5):
|
||||||
# SEID v1.1 Table 5-11
|
# SEID v1.1 Table 5-11
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandGetDeviceConfigDO(BER_TLV_IE, tag=0xf8):
|
class CommandGetDeviceConfigDO(BER_TLV_IE, tag=0xf8):
|
||||||
# SEID v1.1 Table 5-12
|
# SEID v1.1 Table 5-12
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResponseAracAidDO(BER_TLV_IE, tag=0xff70, nested=[AidRefDO, AidRefEmptyDO]):
|
class ResponseAracAidDO(BER_TLV_IE, tag=0xff70, nested=[AidRefDO, AidRefEmptyDO]):
|
||||||
# SEID v1.1 Table 5-13
|
# SEID v1.1 Table 5-13
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BlockDO(BER_TLV_IE, tag=0xe7):
|
class BlockDO(BER_TLV_IE, tag=0xe7):
|
||||||
# SEID v1.1 Table 6-13
|
# SEID v1.1 Table 6-13
|
||||||
_construct = Struct('offset'/Int16ub, 'length'/Int8ub)
|
_construct = Struct('offset'/Int16ub, 'length'/Int8ub)
|
||||||
@@ -197,11 +228,15 @@ class GetCommandDoCollection(TLV_IE_Collection, nested=[RefDO, DeviceConfigDO]):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# SEID v1.1 Table 4-2
|
# SEID v1.1 Table 4-2
|
||||||
|
|
||||||
|
|
||||||
class GetResponseDoCollection(TLV_IE_Collection, nested=[ResponseAllRefArDO, ResponseArDO,
|
class GetResponseDoCollection(TLV_IE_Collection, nested=[ResponseAllRefArDO, ResponseArDO,
|
||||||
ResponseRefreshTagDO, ResponseAramConfigDO]):
|
ResponseRefreshTagDO, ResponseAramConfigDO]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# SEID v1.1 Table 5-1
|
# SEID v1.1 Table 5-1
|
||||||
|
|
||||||
|
|
||||||
class StoreCommandDoCollection(TLV_IE_Collection,
|
class StoreCommandDoCollection(TLV_IE_Collection,
|
||||||
nested=[BlockDO, CommandStoreRefArDO, CommandDelete,
|
nested=[BlockDO, CommandStoreRefArDO, CommandDelete,
|
||||||
CommandUpdateRefreshTagDO, CommandRegisterClientAidsDO,
|
CommandUpdateRefreshTagDO, CommandRegisterClientAidsDO,
|
||||||
@@ -215,6 +250,7 @@ class StoreResponseDoCollection(TLV_IE_Collection,
|
|||||||
nested=[ResponseAllRefArDO, ResponseAracAidDO, ResponseDeviceConfigDO]):
|
nested=[ResponseAllRefArDO, ResponseAracAidDO, ResponseDeviceConfigDO]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ADF_ARAM(CardADF):
|
class ADF_ARAM(CardADF):
|
||||||
def __init__(self, aid='a00000015141434c00', name='ADF.ARA-M', fid=None, sfid=None,
|
def __init__(self, aid='a00000015141434c00', name='ADF.ARA-M', fid=None, sfid=None,
|
||||||
desc='ARA-M Application'):
|
desc='ARA-M Application'):
|
||||||
@@ -259,7 +295,8 @@ class ADF_ARAM(CardADF):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config(tp, v_major=0, v_minor=0, v_patch=1):
|
def get_config(tp, v_major=0, v_minor=0, v_patch=1):
|
||||||
cmd_do = DeviceConfigDO()
|
cmd_do = DeviceConfigDO()
|
||||||
cmd_do.from_dict([{'DeviceInterfaceVersionDO': {'major': v_major, 'minor': v_minor, 'patch': v_patch }}])
|
cmd_do.from_dict([{'DeviceInterfaceVersionDO': {
|
||||||
|
'major': v_major, 'minor': v_minor, 'patch': v_patch}}])
|
||||||
return ADF_ARAM.xceive_apdu_tlv(tp, '80cadf21', cmd_do, ResponseAramConfigDO)
|
return ADF_ARAM.xceive_apdu_tlv(tp, '80cadf21', cmd_do, ResponseAramConfigDO)
|
||||||
|
|
||||||
@with_default_category('Application-Specific Commands')
|
@with_default_category('Application-Specific Commands')
|
||||||
@@ -281,20 +318,30 @@ class ADF_ARAM(CardADF):
|
|||||||
|
|
||||||
store_ref_ar_do_parse = argparse.ArgumentParser()
|
store_ref_ar_do_parse = argparse.ArgumentParser()
|
||||||
# REF-DO
|
# REF-DO
|
||||||
store_ref_ar_do_parse.add_argument('--device-app-id', required=True, help='Identifies the specific device application that the rule appplies to. Hash of Certificate of Application Provider, or UUID. (20/32 hex bytes)')
|
store_ref_ar_do_parse.add_argument(
|
||||||
|
'--device-app-id', required=True, help='Identifies the specific device application that the rule appplies to. Hash of Certificate of Application Provider, or UUID. (20/32 hex bytes)')
|
||||||
aid_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
|
aid_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
|
||||||
aid_grp.add_argument('--aid', help='Identifies the specific SE application for which rules are to be stored. Can be a partial AID, containing for example only the RID. (5-16 hex bytes)')
|
aid_grp.add_argument(
|
||||||
aid_grp.add_argument('--aid-empty', action='store_true', help='No specific SE application, applies to all applications')
|
'--aid', help='Identifies the specific SE application for which rules are to be stored. Can be a partial AID, containing for example only the RID. (5-16 hex bytes)')
|
||||||
store_ref_ar_do_parse.add_argument('--pkg-ref', help='Full Android Java package name (up to 127 chars ASCII)')
|
aid_grp.add_argument('--aid-empty', action='store_true',
|
||||||
|
help='No specific SE application, applies to all applications')
|
||||||
|
store_ref_ar_do_parse.add_argument(
|
||||||
|
'--pkg-ref', help='Full Android Java package name (up to 127 chars ASCII)')
|
||||||
# AR-DO
|
# AR-DO
|
||||||
apdu_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
|
apdu_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
|
||||||
apdu_grp.add_argument('--apdu-never', action='store_true', help='APDU access is not allowed')
|
apdu_grp.add_argument(
|
||||||
apdu_grp.add_argument('--apdu-always', action='store_true', help='APDU access is allowed')
|
'--apdu-never', action='store_true', help='APDU access is not allowed')
|
||||||
apdu_grp.add_argument('--apdu-filter', help='APDU filter: 4 byte CLA/INS/P1/P2 followed by 4 byte mask (8 hex bytes)')
|
apdu_grp.add_argument(
|
||||||
|
'--apdu-always', action='store_true', help='APDU access is allowed')
|
||||||
|
apdu_grp.add_argument(
|
||||||
|
'--apdu-filter', help='APDU filter: 4 byte CLA/INS/P1/P2 followed by 4 byte mask (8 hex bytes)')
|
||||||
nfc_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
|
nfc_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
|
||||||
nfc_grp.add_argument('--nfc-always', action='store_true', help='NFC event access is allowed')
|
nfc_grp.add_argument('--nfc-always', action='store_true',
|
||||||
nfc_grp.add_argument('--nfc-never', action='store_true', help='NFC event access is not allowed')
|
help='NFC event access is allowed')
|
||||||
store_ref_ar_do_parse.add_argument('--android-permissions', help='Android UICC Carrier Privilege Permissions (8 hex bytes)')
|
nfc_grp.add_argument('--nfc-never', action='store_true',
|
||||||
|
help='NFC event access is not allowed')
|
||||||
|
store_ref_ar_do_parse.add_argument(
|
||||||
|
'--android-permissions', help='Android UICC Carrier Privilege Permissions (8 hex bytes)')
|
||||||
|
|
||||||
@cmd2.with_argparser(store_ref_ar_do_parse)
|
@cmd2.with_argparser(store_ref_ar_do_parse)
|
||||||
def do_aram_store_ref_ar_do(self, opts):
|
def do_aram_store_ref_ar_do(self, opts):
|
||||||
@@ -359,6 +406,7 @@ sw_aram = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CardApplicationARAM(CardApplication):
|
class CardApplicationARAM(CardApplication):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('ARA-M', adf=ADF_ARAM(), sw=sw_aram)
|
super().__init__('ARA-M', adf=ADF_ARAM(), sw=sw_aram)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
class CardHandlerBase:
|
class CardHandlerBase:
|
||||||
"""Abstract base class representing a mechanism for card insertion/removal."""
|
"""Abstract base class representing a mechanism for card insertion/removal."""
|
||||||
|
|
||||||
@@ -116,7 +117,8 @@ class CardHandlerAuto(CardHandlerBase):
|
|||||||
def __exec_cmd(self, command):
|
def __exec_cmd(self, command):
|
||||||
print("Card handler Commandline: " + str(command))
|
print("Card handler Commandline: " + str(command))
|
||||||
|
|
||||||
proc = subprocess.Popen([command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
proc = subprocess.Popen(
|
||||||
|
[command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||||
out = proc.communicate()
|
out = proc.communicate()
|
||||||
rc = proc.returncode
|
rc = proc.returncode
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,12 @@ import csv
|
|||||||
|
|
||||||
card_key_providers = [] # type: List['CardKeyProvider']
|
card_key_providers = [] # type: List['CardKeyProvider']
|
||||||
|
|
||||||
|
|
||||||
class CardKeyProvider(abc.ABC):
|
class CardKeyProvider(abc.ABC):
|
||||||
"""Base class, not containing any concrete implementation."""
|
"""Base class, not containing any concrete implementation."""
|
||||||
|
|
||||||
VALID_FIELD_NAMES = ['ICCID', 'ADM1', 'IMSI', 'PIN1', 'PIN2', 'PUK1', 'PUK2']
|
VALID_FIELD_NAMES = ['ICCID', 'ADM1',
|
||||||
|
'IMSI', 'PIN1', 'PIN2', 'PUK1', 'PUK2']
|
||||||
|
|
||||||
# check input parameters, but do nothing concrete yet
|
# check input parameters, but do nothing concrete yet
|
||||||
def _verify_get_data(self, fields: List[str] = [], key: str = 'ICCID', value: str = "") -> Dict[str, str]:
|
def _verify_get_data(self, fields: List[str] = [], key: str = 'ICCID', value: str = "") -> Dict[str, str]:
|
||||||
@@ -80,6 +82,7 @@ class CardKeyProvider(abc.ABC):
|
|||||||
dictionary of {field, value} strings for each requested field from 'fields'
|
dictionary of {field, value} strings for each requested field from 'fields'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CardKeyProviderCsv(CardKeyProvider):
|
class CardKeyProviderCsv(CardKeyProvider):
|
||||||
"""Card key provider implementation that allows to query against a specified CSV file"""
|
"""Card key provider implementation that allows to query against a specified CSV file"""
|
||||||
csv_file = None
|
csv_file = None
|
||||||
@@ -101,7 +104,8 @@ class CardKeyProviderCsv(CardKeyProvider):
|
|||||||
self.csv_file.seek(0)
|
self.csv_file.seek(0)
|
||||||
cr = csv.DictReader(self.csv_file)
|
cr = csv.DictReader(self.csv_file)
|
||||||
if not cr:
|
if not cr:
|
||||||
raise RuntimeError("Could not open DictReader for CSV-File '%s'" % self.filename)
|
raise RuntimeError(
|
||||||
|
"Could not open DictReader for CSV-File '%s'" % self.filename)
|
||||||
cr.fieldnames = [field.upper() for field in cr.fieldnames]
|
cr.fieldnames = [field.upper() for field in cr.fieldnames]
|
||||||
|
|
||||||
rc = {}
|
rc = {}
|
||||||
@@ -141,7 +145,8 @@ def card_key_provider_get(fields, key:str, value:str, provider_list=card_key_pro
|
|||||||
"""
|
"""
|
||||||
for p in provider_list:
|
for p in provider_list:
|
||||||
if not isinstance(p, CardKeyProvider):
|
if not isinstance(p, CardKeyProvider):
|
||||||
raise ValueError("provider list contains element which is not a card data provier")
|
raise ValueError(
|
||||||
|
"provider list contains element which is not a card data provier")
|
||||||
result = p.get(fields, key, value)
|
result = p.get(fields, key, value)
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
@@ -161,7 +166,8 @@ def card_key_provider_get_field(field:str, key:str, value:str, provider_list=car
|
|||||||
"""
|
"""
|
||||||
for p in provider_list:
|
for p in provider_list:
|
||||||
if not isinstance(p, CardKeyProvider):
|
if not isinstance(p, CardKeyProvider):
|
||||||
raise ValueError("provider list contains element which is not a card data provier")
|
raise ValueError(
|
||||||
|
"provider list contains element which is not a card data provier")
|
||||||
result = p.get_field(field, key, value)
|
result = p.get_field(field, key, value)
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
|
|||||||
148
pySim/cards.py
148
pySim/cards.py
@@ -32,6 +32,7 @@ from pySim.utils import *
|
|||||||
from smartcard.util import toBytes
|
from smartcard.util import toBytes
|
||||||
from pytlv.TLV import *
|
from pytlv.TLV import *
|
||||||
|
|
||||||
|
|
||||||
def format_addr(addr: str, addr_type: str) -> str:
|
def format_addr(addr: str, addr_type: str) -> str:
|
||||||
"""
|
"""
|
||||||
helper function to format an FQDN (addr_type = '00') or IPv4
|
helper function to format an FQDN (addr_type = '00') or IPv4
|
||||||
@@ -50,6 +51,7 @@ def format_addr(addr:str, addr_type:str) -> str:
|
|||||||
res += "\t%s # %s\n" % (addr_hex, addr)
|
res += "\t%s # %s\n" % (addr_hex, addr)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class SimCard(object):
|
class SimCard(object):
|
||||||
|
|
||||||
name = 'SIM'
|
name = 'SIM'
|
||||||
@@ -126,7 +128,8 @@ class SimCard(object):
|
|||||||
size = len(data[0]) // 2
|
size = len(data[0]) // 2
|
||||||
hplmn = enc_plmn(mcc, mnc)
|
hplmn = enc_plmn(mcc, mnc)
|
||||||
content = hplmn + access_tech
|
content = hplmn + access_tech
|
||||||
data, sw = self._scc.update_binary(EF['HPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
|
data, sw = self._scc.update_binary(
|
||||||
|
EF['HPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_oplmn_act(self):
|
def read_oplmn_act(self):
|
||||||
@@ -142,7 +145,8 @@ class SimCard(object):
|
|||||||
size = len(data[0]) // 2
|
size = len(data[0]) // 2
|
||||||
hplmn = enc_plmn(mcc, mnc)
|
hplmn = enc_plmn(mcc, mnc)
|
||||||
content = hplmn + access_tech
|
content = hplmn + access_tech
|
||||||
data, sw = self._scc.update_binary(EF['OPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
|
data, sw = self._scc.update_binary(
|
||||||
|
EF['OPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_plmn_act(self):
|
def read_plmn_act(self):
|
||||||
@@ -158,14 +162,16 @@ class SimCard(object):
|
|||||||
size = len(data[0]) // 2
|
size = len(data[0]) // 2
|
||||||
hplmn = enc_plmn(mcc, mnc)
|
hplmn = enc_plmn(mcc, mnc)
|
||||||
content = hplmn + access_tech
|
content = hplmn + access_tech
|
||||||
data, sw = self._scc.update_binary(EF['PLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
|
data, sw = self._scc.update_binary(
|
||||||
|
EF['PLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def update_plmnsel(self, mcc, mnc):
|
def update_plmnsel(self, mcc, mnc):
|
||||||
data = self._scc.read_binary(EF['PLMNsel'], length=None, offset=0)
|
data = self._scc.read_binary(EF['PLMNsel'], length=None, offset=0)
|
||||||
size = len(data[0]) // 2
|
size = len(data[0]) // 2
|
||||||
hplmn = enc_plmn(mcc, mnc)
|
hplmn = enc_plmn(mcc, mnc)
|
||||||
data, sw = self._scc.update_binary(EF['PLMNsel'], hplmn + 'ff' * (size-3))
|
data, sw = self._scc.update_binary(
|
||||||
|
EF['PLMNsel'], hplmn + 'ff' * (size-3))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def update_smsp(self, smsp):
|
def update_smsp(self, smsp):
|
||||||
@@ -193,7 +199,8 @@ class SimCard(object):
|
|||||||
ad = EF_AD()
|
ad = EF_AD()
|
||||||
|
|
||||||
# read from card
|
# read from card
|
||||||
raw_hex_data, sw = self._scc.read_binary(EF['AD'], length=None, offset=0)
|
raw_hex_data, sw = self._scc.read_binary(
|
||||||
|
EF['AD'], length=None, offset=0)
|
||||||
abstract_data = ad.decode_hex(raw_hex_data)
|
abstract_data = ad.decode_hex(raw_hex_data)
|
||||||
|
|
||||||
# perform updates
|
# perform updates
|
||||||
@@ -321,7 +328,8 @@ class SimCard(object):
|
|||||||
def erase_record(self, ef, rec_no):
|
def erase_record(self, ef, rec_no):
|
||||||
"""Erase the contents of a single record"""
|
"""Erase the contents of a single record"""
|
||||||
len = self._scc.record_size(ef)
|
len = self._scc.record_size(ef)
|
||||||
self._scc.update_record(ef, rec_no, "ff" * len, force_len=False, verify=True)
|
self._scc.update_record(ef, rec_no, "ff" * len,
|
||||||
|
force_len=False, verify=True)
|
||||||
|
|
||||||
def set_apdu_parameter(self, cla, sel_ctrl):
|
def set_apdu_parameter(self, cla, sel_ctrl):
|
||||||
"""Set apdu parameters (class byte and selection control bytes)"""
|
"""Set apdu parameters (class byte and selection control bytes)"""
|
||||||
@@ -332,6 +340,7 @@ class SimCard(object):
|
|||||||
"""Get apdu parameters (class byte and selection control bytes)"""
|
"""Get apdu parameters (class byte and selection control bytes)"""
|
||||||
return (self._scc.cla_byte, self._scc.sel_ctrl)
|
return (self._scc.cla_byte, self._scc.sel_ctrl)
|
||||||
|
|
||||||
|
|
||||||
class UsimCard(SimCard):
|
class UsimCard(SimCard):
|
||||||
|
|
||||||
name = 'USIM'
|
name = 'USIM'
|
||||||
@@ -347,7 +356,8 @@ class UsimCard(SimCard):
|
|||||||
return (None, sw)
|
return (None, sw)
|
||||||
|
|
||||||
def update_ehplmn(self, mcc, mnc):
|
def update_ehplmn(self, mcc, mnc):
|
||||||
data = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
|
data = self._scc.read_binary(
|
||||||
|
EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
|
||||||
size = len(data[0]) // 2
|
size = len(data[0]) // 2
|
||||||
ehplmn = enc_plmn(mcc, mnc)
|
ehplmn = enc_plmn(mcc, mnc)
|
||||||
data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
|
data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
|
||||||
@@ -370,7 +380,8 @@ class UsimCard(SimCard):
|
|||||||
if len(epdgid) > 0:
|
if len(epdgid) > 0:
|
||||||
addr_type = get_addr_type(epdgid)
|
addr_type = get_addr_type(epdgid)
|
||||||
if addr_type == None:
|
if addr_type == None:
|
||||||
raise ValueError("Unknown ePDG Id address type or invalid address provided")
|
raise ValueError(
|
||||||
|
"Unknown ePDG Id address type or invalid address provided")
|
||||||
epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
|
epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
|
||||||
else:
|
else:
|
||||||
epdgid_tlv = rpad('ff', size)
|
epdgid_tlv = rpad('ff', size)
|
||||||
@@ -386,13 +397,16 @@ class UsimCard(SimCard):
|
|||||||
return (None, sw)
|
return (None, sw)
|
||||||
|
|
||||||
def update_ePDGSelection(self, mcc, mnc):
|
def update_ePDGSelection(self, mcc, mnc):
|
||||||
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGSelection'], length=None, offset=0)
|
(res, sw) = self._scc.read_binary(
|
||||||
|
EF_USIM_ADF_map['ePDGSelection'], length=None, offset=0)
|
||||||
if sw == '9000' and (len(mcc) == 0 or len(mnc) == 0):
|
if sw == '9000' and (len(mcc) == 0 or len(mnc) == 0):
|
||||||
# Reset contents
|
# Reset contents
|
||||||
# 80 - Tag value
|
# 80 - Tag value
|
||||||
(res, sw) = self._scc.update_binary(EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
|
(res, sw) = self._scc.update_binary(
|
||||||
|
EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
|
||||||
elif sw == '9000':
|
elif sw == '9000':
|
||||||
(res, sw) = self._scc.update_binary(EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
|
(res, sw) = self._scc.update_binary(
|
||||||
|
EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_ust(self):
|
def read_ust(self):
|
||||||
@@ -407,9 +421,11 @@ class UsimCard(SimCard):
|
|||||||
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
|
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
content = enc_st(res, service, bit)
|
content = enc_st(res, service, bit)
|
||||||
(res, sw) = self._scc.update_binary(EF_USIM_ADF_map['UST'], content)
|
(res, sw) = self._scc.update_binary(
|
||||||
|
EF_USIM_ADF_map['UST'], content)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
|
|
||||||
class IsimCard(SimCard):
|
class IsimCard(SimCard):
|
||||||
|
|
||||||
name = 'ISIM'
|
name = 'ISIM'
|
||||||
@@ -429,29 +445,33 @@ class IsimCard(SimCard):
|
|||||||
addr = None
|
addr = None
|
||||||
addr_type = None
|
addr_type = None
|
||||||
content = format_addr(addr, addr_type)
|
content = format_addr(addr, addr_type)
|
||||||
pcscf_recs += "%s" % (len(content) and content or '\tNot available\n')
|
pcscf_recs += "%s" % (len(content)
|
||||||
|
and content or '\tNot available\n')
|
||||||
else:
|
else:
|
||||||
pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (sw)
|
pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (
|
||||||
|
sw)
|
||||||
return pcscf_recs
|
return pcscf_recs
|
||||||
|
|
||||||
def update_pcscf(self, pcscf):
|
def update_pcscf(self, pcscf):
|
||||||
if len(pcscf) > 0:
|
if len(pcscf) > 0:
|
||||||
addr_type = get_addr_type(pcscf)
|
addr_type = get_addr_type(pcscf)
|
||||||
if addr_type == None:
|
if addr_type == None:
|
||||||
raise ValueError("Unknown PCSCF address type or invalid address provided")
|
raise ValueError(
|
||||||
|
"Unknown PCSCF address type or invalid address provided")
|
||||||
content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
|
content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
|
||||||
else:
|
else:
|
||||||
# Just the tag value
|
# Just the tag value
|
||||||
content = '80'
|
content = '80'
|
||||||
rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
|
rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
|
||||||
pcscf_tlv = rpad(content, rec_size_bytes*2)
|
pcscf_tlv = rpad(content, rec_size_bytes*2)
|
||||||
data, sw = self._scc.update_record(EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
|
data, sw = self._scc.update_record(
|
||||||
|
EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_domain(self):
|
def read_domain(self):
|
||||||
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
|
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
# Skip the inital tag value ('80') byte and get length of contents
|
# Skip the initial tag value ('80') byte and get length of contents
|
||||||
length = int(res[2:4], 16)
|
length = int(res[2:4], 16)
|
||||||
content = h2s(res[4:4+(length*2)])
|
content = h2s(res[4:4+(length*2)])
|
||||||
return (content, sw)
|
return (content, sw)
|
||||||
@@ -472,13 +492,14 @@ class IsimCard(SimCard):
|
|||||||
content = tlv.build({'80': hex_str})
|
content = tlv.build({'80': hex_str})
|
||||||
|
|
||||||
bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['DOMAIN'])
|
bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['DOMAIN'])
|
||||||
data, sw = self._scc.update_binary(EF_ISIM_ADF_map['DOMAIN'], rpad(content, bin_size_bytes*2))
|
data, sw = self._scc.update_binary(
|
||||||
|
EF_ISIM_ADF_map['DOMAIN'], rpad(content, bin_size_bytes*2))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_impi(self):
|
def read_impi(self):
|
||||||
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
|
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
# Skip the inital tag value ('80') byte and get length of contents
|
# Skip the initial tag value ('80') byte and get length of contents
|
||||||
length = int(res[2:4], 16)
|
length = int(res[2:4], 16)
|
||||||
content = h2s(res[4:4+(length*2)])
|
content = h2s(res[4:4+(length*2)])
|
||||||
return (content, sw)
|
return (content, sw)
|
||||||
@@ -494,7 +515,8 @@ class IsimCard(SimCard):
|
|||||||
content = tlv.build({'80': hex_str})
|
content = tlv.build({'80': hex_str})
|
||||||
|
|
||||||
bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['IMPI'])
|
bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['IMPI'])
|
||||||
data, sw = self._scc.update_binary(EF_ISIM_ADF_map['IMPI'], rpad(content, bin_size_bytes*2))
|
data, sw = self._scc.update_binary(
|
||||||
|
EF_ISIM_ADF_map['IMPI'], rpad(content, bin_size_bytes*2))
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_impu(self):
|
def read_impu(self):
|
||||||
@@ -503,12 +525,14 @@ class IsimCard(SimCard):
|
|||||||
for i in range(0, rec_cnt):
|
for i in range(0, rec_cnt):
|
||||||
(res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
|
(res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
# Skip the inital tag value ('80') byte and get length of contents
|
# Skip the initial tag value ('80') byte and get length of contents
|
||||||
length = int(res[2:4], 16)
|
length = int(res[2:4], 16)
|
||||||
content = h2s(res[4:4+(length*2)])
|
content = h2s(res[4:4+(length*2)])
|
||||||
impu_recs += "\t%s\n" % (len(content) and content or 'Not available')
|
impu_recs += "\t%s\n" % (len(content)
|
||||||
|
and content or 'Not available')
|
||||||
else:
|
else:
|
||||||
impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (sw)
|
impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (
|
||||||
|
sw)
|
||||||
return impu_recs
|
return impu_recs
|
||||||
|
|
||||||
def update_impu(self, impu=None):
|
def update_impu(self, impu=None):
|
||||||
@@ -521,23 +545,28 @@ class IsimCard(SimCard):
|
|||||||
|
|
||||||
rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
|
rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
|
||||||
impu_tlv = rpad(content, rec_size_bytes*2)
|
impu_tlv = rpad(content, rec_size_bytes*2)
|
||||||
data, sw = self._scc.update_record(EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
|
data, sw = self._scc.update_record(
|
||||||
|
EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def read_iari(self):
|
def read_iari(self):
|
||||||
rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
|
rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
|
||||||
uiari_recs = ""
|
uiari_recs = ""
|
||||||
for i in range(0, rec_cnt):
|
for i in range(0, rec_cnt):
|
||||||
(res, sw) = self._scc.read_record(EF_ISIM_ADF_map['UICCIARI'], i + 1)
|
(res, sw) = self._scc.read_record(
|
||||||
|
EF_ISIM_ADF_map['UICCIARI'], i + 1)
|
||||||
if sw == '9000':
|
if sw == '9000':
|
||||||
# Skip the inital tag value ('80') byte and get length of contents
|
# Skip the initial tag value ('80') byte and get length of contents
|
||||||
length = int(res[2:4], 16)
|
length = int(res[2:4], 16)
|
||||||
content = h2s(res[4:4+(length*2)])
|
content = h2s(res[4:4+(length*2)])
|
||||||
uiari_recs += "\t%s\n" % (len(content) and content or 'Not available')
|
uiari_recs += "\t%s\n" % (len(content)
|
||||||
|
and content or 'Not available')
|
||||||
else:
|
else:
|
||||||
uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (sw)
|
uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (
|
||||||
|
sw)
|
||||||
return uiari_recs
|
return uiari_recs
|
||||||
|
|
||||||
|
|
||||||
class MagicSimBase(abc.ABC, SimCard):
|
class MagicSimBase(abc.ABC, SimCard):
|
||||||
"""
|
"""
|
||||||
Theses cards uses several record based EFs to store the provider infos,
|
Theses cards uses several record based EFs to store the provider infos,
|
||||||
@@ -600,7 +629,8 @@ class MagicSimBase(abc.ABC, SimCard):
|
|||||||
|
|
||||||
# Operator name ( 3f00/7f4d/8f0c )
|
# Operator name ( 3f00/7f4d/8f0c )
|
||||||
self._scc.update_record(self._files['name'][0], 2,
|
self._scc.update_record(self._files['name'][0], 2,
|
||||||
rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01'
|
rpad(b2h(p['name']), 32) + ('%02x' %
|
||||||
|
len(p['name'])) + '01'
|
||||||
)
|
)
|
||||||
|
|
||||||
# ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
|
# ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
|
||||||
@@ -830,6 +860,7 @@ class SysmoSIMgr1(GrcardSim):
|
|||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class SysmoUSIMgr1(UsimCard):
|
class SysmoUSIMgr1(UsimCard):
|
||||||
"""
|
"""
|
||||||
sysmocom sysmoUSIM-GR1
|
sysmocom sysmoUSIM-GR1
|
||||||
@@ -845,7 +876,8 @@ class SysmoUSIMgr1(UsimCard):
|
|||||||
# TODO: check if verify_chv could be used or what it needs
|
# TODO: check if verify_chv could be used or what it needs
|
||||||
# self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
|
# self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
|
||||||
# Unlock the card..
|
# Unlock the card..
|
||||||
data, sw = self._scc._tp.send_apdu_checksw("0020000A083332323133323332")
|
data, sw = self._scc._tp.send_apdu_checksw(
|
||||||
|
"0020000A083332323133323332")
|
||||||
|
|
||||||
# TODO: move into SimCardCommands
|
# TODO: move into SimCardCommands
|
||||||
par = (p['ki'] + # 16b K
|
par = (p['ki'] + # 16b K
|
||||||
@@ -956,7 +988,8 @@ class SysmoUSIMSJS1(UsimCard):
|
|||||||
def verify_adm(self, key):
|
def verify_adm(self, key):
|
||||||
# authenticate as ADM using default key (written on the card..)
|
# authenticate as ADM using default key (written on the card..)
|
||||||
if not key:
|
if not key:
|
||||||
raise ValueError("Please provide a PIN-ADM as there is no default one")
|
raise ValueError(
|
||||||
|
"Please provide a PIN-ADM as there is no default one")
|
||||||
(res, sw) = self._scc.verify_chv(0x0A, key)
|
(res, sw) = self._scc.verify_chv(0x0A, key)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
@@ -1027,7 +1060,8 @@ class SysmoUSIMSJS1(UsimCard):
|
|||||||
# EF.SMSP
|
# EF.SMSP
|
||||||
if p.get('smsp'):
|
if p.get('smsp'):
|
||||||
r = self._scc.select_path(['3f00', '7f10'])
|
r = self._scc.select_path(['3f00', '7f10'])
|
||||||
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
|
data, sw = self._scc.update_record(
|
||||||
|
'6f42', 1, lpad(p['smsp'], 104), force_len=True)
|
||||||
|
|
||||||
# EF.MSISDN
|
# EF.MSISDN
|
||||||
# TODO: Alpha Identifier (currently 'ff'O * 20)
|
# TODO: Alpha Identifier (currently 'ff'O * 20)
|
||||||
@@ -1069,7 +1103,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
self._adm_chv_num = 0x11
|
self._adm_chv_num = 0x11
|
||||||
self._adm2_chv_num = 0x12
|
self._adm2_chv_num = 0x12
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def autodetect(kls, scc):
|
def autodetect(kls, scc):
|
||||||
try:
|
try:
|
||||||
@@ -1080,7 +1113,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def verify_adm2(self, key):
|
def verify_adm2(self, key):
|
||||||
'''
|
'''
|
||||||
Authenticate with ADM2 key.
|
Authenticate with ADM2 key.
|
||||||
@@ -1093,7 +1125,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
(res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
|
(res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
|
|
||||||
def read_ki(self):
|
def read_ki(self):
|
||||||
"""
|
"""
|
||||||
Read Ki in proprietary file.
|
Read Ki in proprietary file.
|
||||||
@@ -1102,7 +1133,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
"""
|
"""
|
||||||
return self._scc.read_binary(self._EF['Ki'])
|
return self._scc.read_binary(self._EF['Ki'])
|
||||||
|
|
||||||
|
|
||||||
def update_ki(self, ki):
|
def update_ki(self, ki):
|
||||||
"""
|
"""
|
||||||
Set Ki in proprietary file.
|
Set Ki in proprietary file.
|
||||||
@@ -1112,7 +1142,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
data, sw = self._scc.update_binary(self._EF['Ki'], ki)
|
data, sw = self._scc.update_binary(self._EF['Ki'], ki)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
|
|
||||||
def read_op_opc(self):
|
def read_op_opc(self):
|
||||||
"""
|
"""
|
||||||
Read Ki in proprietary file.
|
Read Ki in proprietary file.
|
||||||
@@ -1123,7 +1152,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
type = 'OP' if ef[0:2] == '00' else 'OPC'
|
type = 'OP' if ef[0:2] == '00' else 'OPC'
|
||||||
return ((type, ef[2:]), sw)
|
return ((type, ef[2:]), sw)
|
||||||
|
|
||||||
|
|
||||||
def update_op(self, op):
|
def update_op(self, op):
|
||||||
"""
|
"""
|
||||||
Set OP in proprietary file.
|
Set OP in proprietary file.
|
||||||
@@ -1134,7 +1162,6 @@ class FairwavesSIM(UsimCard):
|
|||||||
data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
|
data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
|
|
||||||
def update_opc(self, opc):
|
def update_opc(self, opc):
|
||||||
"""
|
"""
|
||||||
Set OPC in proprietary file.
|
Set OPC in proprietary file.
|
||||||
@@ -1166,7 +1193,8 @@ class FairwavesSIM(UsimCard):
|
|||||||
def _program(self, p):
|
def _program(self, p):
|
||||||
# authenticate as ADM1
|
# authenticate as ADM1
|
||||||
if not p['pin_adm']:
|
if not p['pin_adm']:
|
||||||
raise ValueError("Please provide a PIN-ADM as there is no default one")
|
raise ValueError(
|
||||||
|
"Please provide a PIN-ADM as there is no default one")
|
||||||
self.verify_adm(h2b(p['pin_adm']))
|
self.verify_adm(h2b(p['pin_adm']))
|
||||||
|
|
||||||
# TODO: Set operator name
|
# TODO: Set operator name
|
||||||
@@ -1196,6 +1224,7 @@ class FairwavesSIM(UsimCard):
|
|||||||
if sw != '9000':
|
if sw != '9000':
|
||||||
print("Programming ACC failed with code %s" % sw)
|
print("Programming ACC failed with code %s" % sw)
|
||||||
|
|
||||||
|
|
||||||
class OpenCellsSim(SimCard):
|
class OpenCellsSim(SimCard):
|
||||||
"""
|
"""
|
||||||
OpenCellsSim
|
OpenCellsSim
|
||||||
@@ -1208,7 +1237,6 @@ class OpenCellsSim(SimCard):
|
|||||||
super(OpenCellsSim, self).__init__(ssc)
|
super(OpenCellsSim, self).__init__(ssc)
|
||||||
self._adm_chv_num = 0x0A
|
self._adm_chv_num = 0x0A
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def autodetect(kls, scc):
|
def autodetect(kls, scc):
|
||||||
try:
|
try:
|
||||||
@@ -1219,10 +1247,10 @@ class OpenCellsSim(SimCard):
|
|||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def program(self, p):
|
def program(self, p):
|
||||||
if not p['pin_adm']:
|
if not p['pin_adm']:
|
||||||
raise ValueError("Please provide a PIN-ADM as there is no default one")
|
raise ValueError(
|
||||||
|
"Please provide a PIN-ADM as there is no default one")
|
||||||
self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
|
self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
|
||||||
|
|
||||||
# select MF
|
# select MF
|
||||||
@@ -1245,6 +1273,7 @@ class OpenCellsSim(SimCard):
|
|||||||
# write EF.IMSI
|
# write EF.IMSI
|
||||||
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
|
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
|
||||||
|
|
||||||
|
|
||||||
class WavemobileSim(UsimCard):
|
class WavemobileSim(UsimCard):
|
||||||
"""
|
"""
|
||||||
WavemobileSim
|
WavemobileSim
|
||||||
@@ -1271,23 +1300,27 @@ class WavemobileSim(UsimCard):
|
|||||||
|
|
||||||
def program(self, p):
|
def program(self, p):
|
||||||
if not p['pin_adm']:
|
if not p['pin_adm']:
|
||||||
raise ValueError("Please provide a PIN-ADM as there is no default one")
|
raise ValueError(
|
||||||
|
"Please provide a PIN-ADM as there is no default one")
|
||||||
self.verify_adm(h2b(p['pin_adm']))
|
self.verify_adm(h2b(p['pin_adm']))
|
||||||
|
|
||||||
# EF.ICCID
|
# EF.ICCID
|
||||||
# TODO: Add programming of the ICCID
|
# TODO: Add programming of the ICCID
|
||||||
if p.get('iccid'):
|
if p.get('iccid'):
|
||||||
print("Warning: Programming of the ICCID is not implemented for this type of card.")
|
print(
|
||||||
|
"Warning: Programming of the ICCID is not implemented for this type of card.")
|
||||||
|
|
||||||
# KI (Presumably a propritary file)
|
# KI (Presumably a propritary file)
|
||||||
# TODO: Add programming of KI
|
# TODO: Add programming of KI
|
||||||
if p.get('ki'):
|
if p.get('ki'):
|
||||||
print("Warning: Programming of the KI is not implemented for this type of card.")
|
print(
|
||||||
|
"Warning: Programming of the KI is not implemented for this type of card.")
|
||||||
|
|
||||||
# OPc (Presumably a propritary file)
|
# OPc (Presumably a propritary file)
|
||||||
# TODO: Add programming of OPc
|
# TODO: Add programming of OPc
|
||||||
if p.get('opc'):
|
if p.get('opc'):
|
||||||
print("Warning: Programming of the OPc is not implemented for this type of card.")
|
print(
|
||||||
|
"Warning: Programming of the OPc is not implemented for this type of card.")
|
||||||
|
|
||||||
# EF.SMSP
|
# EF.SMSP
|
||||||
if p.get('smsp'):
|
if p.get('smsp'):
|
||||||
@@ -1374,7 +1407,8 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
def verify_adm(self, key):
|
def verify_adm(self, key):
|
||||||
# authenticate as ADM using default key (written on the card..)
|
# authenticate as ADM using default key (written on the card..)
|
||||||
if not key:
|
if not key:
|
||||||
raise ValueError("Please provide a PIN-ADM as there is no default one")
|
raise ValueError(
|
||||||
|
"Please provide a PIN-ADM as there is no default one")
|
||||||
(res, sw) = self._scc.verify_chv(0x0A, key)
|
(res, sw) = self._scc.verify_chv(0x0A, key)
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
@@ -1386,7 +1420,8 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
# license management, so the ICCID must be kept at its factory
|
# license management, so the ICCID must be kept at its factory
|
||||||
# setting!
|
# setting!
|
||||||
if p.get('iccid'):
|
if p.get('iccid'):
|
||||||
print("Warning: Programming of the ICCID is not implemented for this type of card.")
|
print(
|
||||||
|
"Warning: Programming of the ICCID is not implemented for this type of card.")
|
||||||
|
|
||||||
# select DF_GSM
|
# select DF_GSM
|
||||||
self._scc.select_path(['7f20'])
|
self._scc.select_path(['7f20'])
|
||||||
@@ -1436,7 +1471,8 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
# EF.SMSP
|
# EF.SMSP
|
||||||
if p.get('smsp'):
|
if p.get('smsp'):
|
||||||
r = self._scc.select_path(['3f00', '7f10'])
|
r = self._scc.select_path(['3f00', '7f10'])
|
||||||
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
|
data, sw = self._scc.update_record(
|
||||||
|
'6f42', 1, lpad(p['smsp'], 104), force_len=True)
|
||||||
|
|
||||||
# EF.MSISDN
|
# EF.MSISDN
|
||||||
# TODO: Alpha Identifier (currently 'ff'O * 20)
|
# TODO: Alpha Identifier (currently 'ff'O * 20)
|
||||||
@@ -1447,7 +1483,8 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
content = 'ff' * 20 + msisdn
|
content = 'ff' * 20 + msisdn
|
||||||
|
|
||||||
r = self._scc.select_path(['3f00', '7f10'])
|
r = self._scc.select_path(['3f00', '7f10'])
|
||||||
data, sw = self._scc.update_record('6F40', 1, content, force_len=True)
|
data, sw = self._scc.update_record(
|
||||||
|
'6F40', 1, content, force_len=True)
|
||||||
|
|
||||||
# EF.ACC
|
# EF.ACC
|
||||||
if p.get('acc'):
|
if p.get('acc'):
|
||||||
@@ -1484,7 +1521,6 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
if sw != '9000':
|
if sw != '9000':
|
||||||
print("Programming P-CSCF failed with code %s" % sw)
|
print("Programming P-CSCF failed with code %s" % sw)
|
||||||
|
|
||||||
|
|
||||||
# update EF.DOMAIN in ADF.ISIM
|
# update EF.DOMAIN in ADF.ISIM
|
||||||
if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
|
if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
|
||||||
if p.get('ims_hdomain'):
|
if p.get('ims_hdomain'):
|
||||||
@@ -1493,7 +1529,8 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
sw = self.update_domain()
|
sw = self.update_domain()
|
||||||
|
|
||||||
if sw != '9000':
|
if sw != '9000':
|
||||||
print("Programming Home Network Domain Name failed with code %s"%sw)
|
print(
|
||||||
|
"Programming Home Network Domain Name failed with code %s" % sw)
|
||||||
|
|
||||||
# update EF.IMPI in ADF.ISIM
|
# update EF.IMPI in ADF.ISIM
|
||||||
# TODO: Validate IMPI input
|
# TODO: Validate IMPI input
|
||||||
@@ -1544,13 +1581,13 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
|||||||
if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
|
if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
|
||||||
if p.get('epdgSelection'):
|
if p.get('epdgSelection'):
|
||||||
epdg_plmn = p['epdgSelection']
|
epdg_plmn = p['epdgSelection']
|
||||||
sw = self.update_ePDGSelection(epdg_plmn[:3], epdg_plmn[3:])
|
sw = self.update_ePDGSelection(
|
||||||
|
epdg_plmn[:3], epdg_plmn[3:])
|
||||||
else:
|
else:
|
||||||
sw = self.update_ePDGSelection("", "")
|
sw = self.update_ePDGSelection("", "")
|
||||||
if sw != '9000':
|
if sw != '9000':
|
||||||
print("Programming ePDGSelection failed with code %s" % sw)
|
print("Programming ePDGSelection failed with code %s" % sw)
|
||||||
|
|
||||||
|
|
||||||
# After successfully programming EF.ePDGId and EF.ePDGSelection,
|
# After successfully programming EF.ePDGId and EF.ePDGSelection,
|
||||||
# Set service 106 and 107 as available in EF.UST
|
# Set service 106 and 107 as available in EF.UST
|
||||||
# Disable service 95, 99, 115 if ISIM application is present
|
# Disable service 95, 99, 115 if ISIM application is present
|
||||||
@@ -1581,6 +1618,7 @@ _cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
|
|||||||
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
|
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
|
||||||
FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2]
|
FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2]
|
||||||
|
|
||||||
|
|
||||||
def card_detect(ctype, scc):
|
def card_detect(ctype, scc):
|
||||||
# Detect type if needed
|
# Detect type if needed
|
||||||
card = None
|
card = None
|
||||||
|
|||||||
38
pySim/cat.py
38
pySim/cat.py
@@ -24,34 +24,48 @@ from construct import *
|
|||||||
# Tag values as per TS 101 220 Table 7.23
|
# Tag values as per TS 101 220 Table 7.23
|
||||||
|
|
||||||
# TS 102 223 Section 8.1
|
# TS 102 223 Section 8.1
|
||||||
|
|
||||||
|
|
||||||
class Address(COMPR_TLV_IE, tag=0x06):
|
class Address(COMPR_TLV_IE, tag=0x06):
|
||||||
_construct = Struct('ton_npi'/Int8ub,
|
_construct = Struct('ton_npi'/Int8ub,
|
||||||
'call_number'/BcdAdapter(Bytes(this._.total_len-1)))
|
'call_number'/BcdAdapter(Bytes(this._.total_len-1)))
|
||||||
|
|
||||||
# TS 102 223 Section 8.2
|
# TS 102 223 Section 8.2
|
||||||
|
|
||||||
|
|
||||||
class AlphaIdentifier(COMPR_TLV_IE, tag=0x05):
|
class AlphaIdentifier(COMPR_TLV_IE, tag=0x05):
|
||||||
# FIXME: like EF.ADN
|
# FIXME: like EF.ADN
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TS 102 223 Section 8.3
|
# TS 102 223 Section 8.3
|
||||||
|
|
||||||
|
|
||||||
class Subaddress(COMPR_TLV_IE, tag=0x08):
|
class Subaddress(COMPR_TLV_IE, tag=0x08):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TS 102 223 Section 8.4
|
# TS 102 223 Section 8.4
|
||||||
|
|
||||||
|
|
||||||
class CapabilityConfigParams(COMPR_TLV_IE, tag=0x07):
|
class CapabilityConfigParams(COMPR_TLV_IE, tag=0x07):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TS 31.111 Section 8.5
|
# TS 31.111 Section 8.5
|
||||||
|
|
||||||
|
|
||||||
class CBSPage(COMPR_TLV_IE, tag=0x0C):
|
class CBSPage(COMPR_TLV_IE, tag=0x0C):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TS 102 223 Section 8.6
|
# TS 102 223 Section 8.6
|
||||||
|
|
||||||
|
|
||||||
class CommandDetails(COMPR_TLV_IE, tag=0x01):
|
class CommandDetails(COMPR_TLV_IE, tag=0x01):
|
||||||
_construct = Struct('command_number'/Int8ub,
|
_construct = Struct('command_number'/Int8ub,
|
||||||
'type_of_command'/Int8ub,
|
'type_of_command'/Int8ub,
|
||||||
'command_qualifier'/Int8ub)
|
'command_qualifier'/Int8ub)
|
||||||
|
|
||||||
# TS 102 223 Section 8.7
|
# TS 102 223 Section 8.7
|
||||||
|
|
||||||
|
|
||||||
class DeviceIdentities(COMPR_TLV_IE, tag=0x82):
|
class DeviceIdentities(COMPR_TLV_IE, tag=0x82):
|
||||||
DEV_IDS = bidict({
|
DEV_IDS = bidict({
|
||||||
0x01: 'keypad',
|
0x01: 'keypad',
|
||||||
@@ -91,6 +105,7 @@ class DeviceIdentities(COMPR_TLV_IE, tag=0x82):
|
|||||||
0x82: 'terminal',
|
0x82: 'terminal',
|
||||||
0x83: 'network',
|
0x83: 'network',
|
||||||
})
|
})
|
||||||
|
|
||||||
def _from_bytes(self, do: bytes):
|
def _from_bytes(self, do: bytes):
|
||||||
return {'source_dev_id': self.DEV_IDS[do[0]], 'dest_dev_id': self.DEV_IDS[do[1]]}
|
return {'source_dev_id': self.DEV_IDS[do[0]], 'dest_dev_id': self.DEV_IDS[do[1]]}
|
||||||
|
|
||||||
@@ -100,65 +115,84 @@ class DeviceIdentities(COMPR_TLV_IE, tag=0x82):
|
|||||||
return bytes([src, dst])
|
return bytes([src, dst])
|
||||||
|
|
||||||
# TS 102 223 Section 8.8
|
# TS 102 223 Section 8.8
|
||||||
|
|
||||||
|
|
||||||
class Duration(COMPR_TLV_IE, tag=0x04):
|
class Duration(COMPR_TLV_IE, tag=0x04):
|
||||||
_construct = Struct('time_unit'/Int8ub,
|
_construct = Struct('time_unit'/Int8ub,
|
||||||
'time_interval'/Int8ub)
|
'time_interval'/Int8ub)
|
||||||
|
|
||||||
# TS 102 223 Section 8.9
|
# TS 102 223 Section 8.9
|
||||||
|
|
||||||
|
|
||||||
class Item(COMPR_TLV_IE, tag=0x0f):
|
class Item(COMPR_TLV_IE, tag=0x0f):
|
||||||
_construct = Struct('identifier'/Int8ub,
|
_construct = Struct('identifier'/Int8ub,
|
||||||
'text_string'/GsmStringAdapter(GreedyBytes))
|
'text_string'/GsmStringAdapter(GreedyBytes))
|
||||||
|
|
||||||
# TS 102 223 Section 8.10
|
# TS 102 223 Section 8.10
|
||||||
|
|
||||||
|
|
||||||
class ItemIdentifier(COMPR_TLV_IE, tag=0x10):
|
class ItemIdentifier(COMPR_TLV_IE, tag=0x10):
|
||||||
_construct = Struct('identifier'/Int8ub)
|
_construct = Struct('identifier'/Int8ub)
|
||||||
|
|
||||||
# TS 102 223 Section 8.11
|
# TS 102 223 Section 8.11
|
||||||
|
|
||||||
|
|
||||||
class ResponseLength(COMPR_TLV_IE, tag=0x11):
|
class ResponseLength(COMPR_TLV_IE, tag=0x11):
|
||||||
_construct = Struct('minimum_length'/Int8ub,
|
_construct = Struct('minimum_length'/Int8ub,
|
||||||
'maximum_length'/Int8ub)
|
'maximum_length'/Int8ub)
|
||||||
|
|
||||||
# TS 102 223 Section 8.12
|
# TS 102 223 Section 8.12
|
||||||
|
|
||||||
|
|
||||||
class Result(COMPR_TLV_IE, tag=0x03):
|
class Result(COMPR_TLV_IE, tag=0x03):
|
||||||
_construct = Struct('general_result'/Int8ub,
|
_construct = Struct('general_result'/Int8ub,
|
||||||
'additional_information'/HexAdapter(GreedyBytes))
|
'additional_information'/HexAdapter(GreedyBytes))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TS 102 223 Section 8.13 + TS 31.111 Section 8.13
|
# TS 102 223 Section 8.13 + TS 31.111 Section 8.13
|
||||||
class SMS_TPDU(COMPR_TLV_IE, tag=0x8B):
|
class SMS_TPDU(COMPR_TLV_IE, tag=0x8B):
|
||||||
_construct = Struct('tpdu'/HexAdapter(GreedyBytes))
|
_construct = Struct('tpdu'/HexAdapter(GreedyBytes))
|
||||||
|
|
||||||
# TS 102 223 Section 8.15
|
# TS 102 223 Section 8.15
|
||||||
|
|
||||||
|
|
||||||
class TextString(COMPR_TLV_IE, tag=0x0d):
|
class TextString(COMPR_TLV_IE, tag=0x0d):
|
||||||
_construct = Struct('dcs'/Int8ub,
|
_construct = Struct('dcs'/Int8ub,
|
||||||
'text_string'/HexAdapter(GreedyBytes))
|
'text_string'/HexAdapter(GreedyBytes))
|
||||||
|
|
||||||
# TS 102 223 Section 8.16
|
# TS 102 223 Section 8.16
|
||||||
|
|
||||||
|
|
||||||
class Tone(COMPR_TLV_IE, tag=0x0e):
|
class Tone(COMPR_TLV_IE, tag=0x0e):
|
||||||
_construct = Struct('tone'/Int8ub)
|
_construct = Struct('tone'/Int8ub)
|
||||||
|
|
||||||
# TS 31 111 Section 8.17
|
# TS 31 111 Section 8.17
|
||||||
|
|
||||||
|
|
||||||
class USSDString(COMPR_TLV_IE, tag=0x0a):
|
class USSDString(COMPR_TLV_IE, tag=0x0a):
|
||||||
_construct = Struct('dcs'/Int8ub,
|
_construct = Struct('dcs'/Int8ub,
|
||||||
'ussd_string'/HexAdapter(GreedyBytes))
|
'ussd_string'/HexAdapter(GreedyBytes))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TS 101 220 Table 7.17
|
# TS 101 220 Table 7.17
|
||||||
class ProactiveCommand(BER_TLV_IE, tag=0xD0):
|
class ProactiveCommand(BER_TLV_IE, tag=0xD0):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TS 101 220 Table 7.17 + 31.111 7.1.1.2
|
# TS 101 220 Table 7.17 + 31.111 7.1.1.2
|
||||||
|
|
||||||
|
|
||||||
class SMSPPDownload(BER_TLV_IE, tag=0xD1,
|
class SMSPPDownload(BER_TLV_IE, tag=0xD1,
|
||||||
nested=[DeviceIdentities, Address, SMS_TPDU]):
|
nested=[DeviceIdentities, Address, SMS_TPDU]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TS 101 220 Table 7.17 + 31.111 7.1.1.3
|
# TS 101 220 Table 7.17 + 31.111 7.1.1.3
|
||||||
|
|
||||||
|
|
||||||
class SMSCBDownload(BER_TLV_IE, tag=0xD2,
|
class SMSCBDownload(BER_TLV_IE, tag=0xD2,
|
||||||
nested=[DeviceIdentities, CBSPage]):
|
nested=[DeviceIdentities, CBSPage]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class USSDDownload(BER_TLV_IE, tag=0xD9,
|
class USSDDownload(BER_TLV_IE, tag=0xD9,
|
||||||
nested=[DeviceIdentities, USSDString]):
|
nested=[DeviceIdentities, USSDString]):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from pySim.construct import LV
|
|||||||
from pySim.utils import rpad, b2h, h2b, sw_match, bertlv_encode_len, Hexstr, h2i, str_sanitize
|
from pySim.utils import rpad, b2h, h2b, sw_match, bertlv_encode_len, Hexstr, h2i, str_sanitize
|
||||||
from pySim.exceptions import SwMatchError
|
from pySim.exceptions import SwMatchError
|
||||||
|
|
||||||
|
|
||||||
class SimCardCommands(object):
|
class SimCardCommands(object):
|
||||||
def __init__(self, transport):
|
def __init__(self, transport):
|
||||||
self._tp = transport
|
self._tp = transport
|
||||||
@@ -37,13 +38,15 @@ class SimCardCommands(object):
|
|||||||
# see also: ETSI TS 102 221, chapter 11.1.1.3.1 Response for MF,
|
# see also: ETSI TS 102 221, chapter 11.1.1.3.1 Response for MF,
|
||||||
# DF or ADF
|
# DF or ADF
|
||||||
from pytlv.TLV import TLV
|
from pytlv.TLV import TLV
|
||||||
tlvparser = TLV(['82', '83', '84', 'a5', '8a', '8b', '8c', '80', 'ab', 'c6', '81', '88'])
|
tlvparser = TLV(['82', '83', '84', 'a5', '8a', '8b',
|
||||||
|
'8c', '80', 'ab', 'c6', '81', '88'])
|
||||||
|
|
||||||
# pytlv is case sensitive!
|
# pytlv is case sensitive!
|
||||||
fcp = fcp.lower()
|
fcp = fcp.lower()
|
||||||
|
|
||||||
if fcp[0:2] != '62':
|
if fcp[0:2] != '62':
|
||||||
raise ValueError('Tag of the FCP template does not match, expected 62 but got %s'%fcp[0:2])
|
raise ValueError(
|
||||||
|
'Tag of the FCP template does not match, expected 62 but got %s' % fcp[0:2])
|
||||||
|
|
||||||
# Unfortunately the spec is not very clear if the FCP length is
|
# Unfortunately the spec is not very clear if the FCP length is
|
||||||
# coded as one or two byte vale, so we have to try it out by
|
# coded as one or two byte vale, so we have to try it out by
|
||||||
@@ -100,7 +103,8 @@ class SimCardCommands(object):
|
|||||||
if type(dir_list) is not list:
|
if type(dir_list) is not list:
|
||||||
dir_list = [dir_list]
|
dir_list = [dir_list]
|
||||||
for i in dir_list:
|
for i in dir_list:
|
||||||
data, sw = self._tp.send_apdu(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
|
data, sw = self._tp.send_apdu(
|
||||||
|
self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
|
||||||
rv.append((data, sw))
|
rv.append((data, sw))
|
||||||
if sw != '9000':
|
if sw != '9000':
|
||||||
return rv
|
return rv
|
||||||
@@ -162,11 +166,13 @@ class SimCardCommands(object):
|
|||||||
chunk_offset = 0
|
chunk_offset = 0
|
||||||
while chunk_offset < length:
|
while chunk_offset < length:
|
||||||
chunk_len = min(255, length-chunk_offset)
|
chunk_len = min(255, length-chunk_offset)
|
||||||
pdu = self.cla_byte + 'b0%04x%02x' % (offset + chunk_offset, chunk_len)
|
pdu = self.cla_byte + \
|
||||||
|
'b0%04x%02x' % (offset + chunk_offset, chunk_len)
|
||||||
try:
|
try:
|
||||||
data, sw = self._tp.send_apdu_checksw(pdu)
|
data, sw = self._tp.send_apdu_checksw(pdu)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError('%s, failed to read (offset %d)' % (str_sanitize(str(e)), offset))
|
raise ValueError('%s, failed to read (offset %d)' %
|
||||||
|
(str_sanitize(str(e)), offset))
|
||||||
total_data += data
|
total_data += data
|
||||||
chunk_offset += chunk_len
|
chunk_offset += chunk_len
|
||||||
return total_data, sw
|
return total_data, sw
|
||||||
@@ -194,11 +200,13 @@ class SimCardCommands(object):
|
|||||||
while chunk_offset < data_length:
|
while chunk_offset < data_length:
|
||||||
chunk_len = min(255, data_length - chunk_offset)
|
chunk_len = min(255, data_length - chunk_offset)
|
||||||
# chunk_offset is bytes, but data slicing is hex chars, so we need to multiply by 2
|
# chunk_offset is bytes, but data slicing is hex chars, so we need to multiply by 2
|
||||||
pdu = self.cla_byte + 'd6%04x%02x' % (offset + chunk_offset, chunk_len) + data[chunk_offset*2 : (chunk_offset+chunk_len)*2]
|
pdu = self.cla_byte + \
|
||||||
|
'd6%04x%02x' % (offset + chunk_offset, chunk_len) + \
|
||||||
|
data[chunk_offset*2: (chunk_offset+chunk_len)*2]
|
||||||
try:
|
try:
|
||||||
chunk_data, chunk_sw = self._tp.send_apdu_checksw(pdu)
|
chunk_data, chunk_sw = self._tp.send_apdu_checksw(pdu)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError('%s, failed to write chunk (chunk_offset %d, chunk_len %d)' % \
|
raise ValueError('%s, failed to write chunk (chunk_offset %d, chunk_len %d)' %
|
||||||
(str_sanitize(str(e)), chunk_offset, chunk_len))
|
(str_sanitize(str(e)), chunk_offset, chunk_len))
|
||||||
total_data += data
|
total_data += data
|
||||||
chunk_offset += chunk_len
|
chunk_offset += chunk_len
|
||||||
@@ -216,7 +224,8 @@ class SimCardCommands(object):
|
|||||||
"""
|
"""
|
||||||
res = self.read_binary(ef, len(data) // 2, offset)
|
res = self.read_binary(ef, len(data) // 2, offset)
|
||||||
if res[0].lower() != data.lower():
|
if res[0].lower() != data.lower():
|
||||||
raise ValueError('Binary verification failed (expected %s, got %s)' % (data.lower(), res[0].lower()))
|
raise ValueError('Binary verification failed (expected %s, got %s)' % (
|
||||||
|
data.lower(), res[0].lower()))
|
||||||
|
|
||||||
def read_record(self, ef, rec_no: int):
|
def read_record(self, ef, rec_no: int):
|
||||||
"""Execute READ RECORD.
|
"""Execute READ RECORD.
|
||||||
@@ -253,7 +262,8 @@ class SimCardCommands(object):
|
|||||||
# exceed we throw an exception.
|
# exceed we throw an exception.
|
||||||
rec_length = self.__record_len(res)
|
rec_length = self.__record_len(res)
|
||||||
if (len(data) // 2 > rec_length):
|
if (len(data) // 2 > rec_length):
|
||||||
raise ValueError('Data length exceeds record length (expected max %d, got %d)' % (rec_length, len(data) // 2))
|
raise ValueError('Data length exceeds record length (expected max %d, got %d)' % (
|
||||||
|
rec_length, len(data) // 2))
|
||||||
elif (len(data) // 2 < rec_length):
|
elif (len(data) // 2 < rec_length):
|
||||||
data = rpad(data, rec_length * 2)
|
data = rpad(data, rec_length * 2)
|
||||||
|
|
||||||
@@ -280,7 +290,8 @@ class SimCardCommands(object):
|
|||||||
"""
|
"""
|
||||||
res = self.read_record(ef, rec_no)
|
res = self.read_record(ef, rec_no)
|
||||||
if res[0].lower() != data.lower():
|
if res[0].lower() != data.lower():
|
||||||
raise ValueError('Record verification failed (expected %s, got %s)' % (data.lower(), res[0].lower()))
|
raise ValueError('Record verification failed (expected %s, got %s)' % (
|
||||||
|
data.lower(), res[0].lower()))
|
||||||
|
|
||||||
def record_size(self, ef):
|
def record_size(self, ef):
|
||||||
"""Determine the record size of given file.
|
"""Determine the record size of given file.
|
||||||
@@ -400,7 +411,8 @@ class SimCardCommands(object):
|
|||||||
# 3GPP TS 31.102 Section 7.1.2.1
|
# 3GPP TS 31.102 Section 7.1.2.1
|
||||||
AuthCmd3G = Struct('rand'/LV, 'autn'/Optional(LV))
|
AuthCmd3G = Struct('rand'/LV, 'autn'/Optional(LV))
|
||||||
AuthResp3GSyncFail = Struct(Const(b'\xDC'), 'auts'/LV)
|
AuthResp3GSyncFail = Struct(Const(b'\xDC'), 'auts'/LV)
|
||||||
AuthResp3GSuccess = Struct(Const(b'\xDB'), 'res'/LV, 'ck'/LV, 'ik'/LV, 'kc'/Optional(LV))
|
AuthResp3GSuccess = Struct(
|
||||||
|
Const(b'\xDB'), 'res'/LV, 'ck'/LV, 'ik'/LV, 'kc'/Optional(LV))
|
||||||
AuthResp3G = Select(AuthResp3GSyncFail, AuthResp3GSuccess)
|
AuthResp3G = Select(AuthResp3GSyncFail, AuthResp3GSuccess)
|
||||||
# build parameters
|
# build parameters
|
||||||
cmd_data = {'rand': rand, 'autn': autn}
|
cmd_data = {'rand': rand, 'autn': autn}
|
||||||
@@ -408,7 +420,8 @@ class SimCardCommands(object):
|
|||||||
p2 = '81'
|
p2 = '81'
|
||||||
elif context == 'gsm':
|
elif context == 'gsm':
|
||||||
p2 = '80'
|
p2 = '80'
|
||||||
(data, sw) = self._tp.send_apdu_constr_checksw(self.cla_byte, '88', '00', p2, AuthCmd3G, cmd_data, AuthResp3G)
|
(data, sw) = self._tp.send_apdu_constr_checksw(
|
||||||
|
self.cla_byte, '88', '00', p2, AuthCmd3G, cmd_data, AuthResp3G)
|
||||||
if 'auts' in data:
|
if 'auts' in data:
|
||||||
ret = {'synchronisation_failure': data}
|
ret = {'synchronisation_failure': data}
|
||||||
else:
|
else:
|
||||||
@@ -464,7 +477,8 @@ class SimCardCommands(object):
|
|||||||
code : chv code as hex string
|
code : chv code as hex string
|
||||||
"""
|
"""
|
||||||
fc = rpad(b2h(code), 16)
|
fc = rpad(b2h(code), 16)
|
||||||
data, sw = self._tp.send_apdu(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)
|
data, sw = self._tp.send_apdu(
|
||||||
|
self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)
|
||||||
self._chv_process_sw('verify', chv_no, code, sw)
|
self._chv_process_sw('verify', chv_no, code, sw)
|
||||||
return (data, sw)
|
return (data, sw)
|
||||||
|
|
||||||
@@ -477,7 +491,8 @@ class SimCardCommands(object):
|
|||||||
pin_code : new chv code as hex string
|
pin_code : new chv code as hex string
|
||||||
"""
|
"""
|
||||||
fc = rpad(b2h(puk_code), 16) + rpad(b2h(pin_code), 16)
|
fc = rpad(b2h(puk_code), 16) + rpad(b2h(pin_code), 16)
|
||||||
data, sw = self._tp.send_apdu(self.cla_byte + '2C00' + ('%02X' % chv_no) + '10' + fc)
|
data, sw = self._tp.send_apdu(
|
||||||
|
self.cla_byte + '2C00' + ('%02X' % chv_no) + '10' + fc)
|
||||||
self._chv_process_sw('unblock', chv_no, pin_code, sw)
|
self._chv_process_sw('unblock', chv_no, pin_code, sw)
|
||||||
return (data, sw)
|
return (data, sw)
|
||||||
|
|
||||||
@@ -490,7 +505,8 @@ class SimCardCommands(object):
|
|||||||
new_pin_code : new chv code as hex string
|
new_pin_code : new chv code as hex string
|
||||||
"""
|
"""
|
||||||
fc = rpad(b2h(pin_code), 16) + rpad(b2h(new_pin_code), 16)
|
fc = rpad(b2h(pin_code), 16) + rpad(b2h(new_pin_code), 16)
|
||||||
data, sw = self._tp.send_apdu(self.cla_byte + '2400' + ('%02X' % chv_no) + '10' + fc)
|
data, sw = self._tp.send_apdu(
|
||||||
|
self.cla_byte + '2400' + ('%02X' % chv_no) + '10' + fc)
|
||||||
self._chv_process_sw('change', chv_no, pin_code, sw)
|
self._chv_process_sw('change', chv_no, pin_code, sw)
|
||||||
return (data, sw)
|
return (data, sw)
|
||||||
|
|
||||||
@@ -503,7 +519,8 @@ class SimCardCommands(object):
|
|||||||
new_pin_code : new chv code as hex string
|
new_pin_code : new chv code as hex string
|
||||||
"""
|
"""
|
||||||
fc = rpad(b2h(pin_code), 16)
|
fc = rpad(b2h(pin_code), 16)
|
||||||
data, sw = self._tp.send_apdu(self.cla_byte + '2600' + ('%02X' % chv_no) + '08' + fc)
|
data, sw = self._tp.send_apdu(
|
||||||
|
self.cla_byte + '2600' + ('%02X' % chv_no) + '08' + fc)
|
||||||
self._chv_process_sw('disable', chv_no, pin_code, sw)
|
self._chv_process_sw('disable', chv_no, pin_code, sw)
|
||||||
return (data, sw)
|
return (data, sw)
|
||||||
|
|
||||||
@@ -515,7 +532,8 @@ class SimCardCommands(object):
|
|||||||
pin_code : chv code as hex string
|
pin_code : chv code as hex string
|
||||||
"""
|
"""
|
||||||
fc = rpad(b2h(pin_code), 16)
|
fc = rpad(b2h(pin_code), 16)
|
||||||
data, sw = self._tp.send_apdu(self.cla_byte + '2800' + ('%02X' % chv_no) + '08' + fc)
|
data, sw = self._tp.send_apdu(
|
||||||
|
self.cla_byte + '2800' + ('%02X' % chv_no) + '08' + fc)
|
||||||
self._chv_process_sw('enable', chv_no, pin_code, sw)
|
self._chv_process_sw('enable', chv_no, pin_code, sw)
|
||||||
return (data, sw)
|
return (data, sw)
|
||||||
|
|
||||||
@@ -556,6 +574,7 @@ class SimCardCommands(object):
|
|||||||
return '01%02x' % (secs // 60)
|
return '01%02x' % (secs // 60)
|
||||||
else:
|
else:
|
||||||
return '00%02x' % secs
|
return '00%02x' % secs
|
||||||
|
|
||||||
def decode_duration(enc: Hexstr) -> int:
|
def decode_duration(enc: Hexstr) -> int:
|
||||||
time_unit = enc[:2]
|
time_unit = enc[:2]
|
||||||
length = h2i(enc[2:4])
|
length = h2i(enc[2:4])
|
||||||
@@ -573,7 +592,8 @@ class SimCardCommands(object):
|
|||||||
raise ValueError('Time unit must be 0x00..0x04')
|
raise ValueError('Time unit must be 0x00..0x04')
|
||||||
min_dur_enc = encode_duration(min_len_secs)
|
min_dur_enc = encode_duration(min_len_secs)
|
||||||
max_dur_enc = encode_duration(max_len_secs)
|
max_dur_enc = encode_duration(max_len_secs)
|
||||||
data, sw = self._tp.send_apdu_checksw('8076000004' + min_dur_enc + max_dur_enc)
|
data, sw = self._tp.send_apdu_checksw(
|
||||||
|
'8076000004' + min_dur_enc + max_dur_enc)
|
||||||
negotiated_duration_secs = decode_duration(data[:4])
|
negotiated_duration_secs = decode_duration(data[:4])
|
||||||
resume_token = data[4:]
|
resume_token = data[4:]
|
||||||
return (negotiated_duration_secs, resume_token, sw)
|
return (negotiated_duration_secs, resume_token, sw)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from construct.lib.containers import Container, ListContainer
|
||||||
|
from construct.core import EnumIntegerString
|
||||||
import typing
|
import typing
|
||||||
from construct import *
|
from construct import *
|
||||||
from pySim.utils import b2h, h2b, swap_nibbles
|
from pySim.utils import b2h, h2b, swap_nibbles
|
||||||
@@ -23,18 +25,24 @@ import gsm0338
|
|||||||
|
|
||||||
class HexAdapter(Adapter):
|
class HexAdapter(Adapter):
|
||||||
"""convert a bytes() type to a string of hex nibbles."""
|
"""convert a bytes() type to a string of hex nibbles."""
|
||||||
|
|
||||||
def _decode(self, obj, context, path):
|
def _decode(self, obj, context, path):
|
||||||
return b2h(obj)
|
return b2h(obj)
|
||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
return h2b(obj)
|
return h2b(obj)
|
||||||
|
|
||||||
|
|
||||||
class BcdAdapter(Adapter):
|
class BcdAdapter(Adapter):
|
||||||
"""convert a bytes() type to a string of BCD nibbles."""
|
"""convert a bytes() type to a string of BCD nibbles."""
|
||||||
|
|
||||||
def _decode(self, obj, context, path):
|
def _decode(self, obj, context, path):
|
||||||
return swap_nibbles(b2h(obj))
|
return swap_nibbles(b2h(obj))
|
||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
return h2b(swap_nibbles(obj))
|
return h2b(swap_nibbles(obj))
|
||||||
|
|
||||||
|
|
||||||
class Rpad(Adapter):
|
class Rpad(Adapter):
|
||||||
"""
|
"""
|
||||||
Encoder appends padding bytes (b'\\xff') up to target size.
|
Encoder appends padding bytes (b'\\xff') up to target size.
|
||||||
@@ -54,9 +62,11 @@ class Rpad(Adapter):
|
|||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
if len(obj) > self.sizeof():
|
if len(obj) > self.sizeof():
|
||||||
raise SizeofError("Input ({}) exceeds target size ({})".format(len(obj), self.sizeof()))
|
raise SizeofError("Input ({}) exceeds target size ({})".format(
|
||||||
|
len(obj), self.sizeof()))
|
||||||
return obj + self.pattern * (self.sizeof() - len(obj))
|
return obj + self.pattern * (self.sizeof() - len(obj))
|
||||||
|
|
||||||
|
|
||||||
class GsmStringAdapter(Adapter):
|
class GsmStringAdapter(Adapter):
|
||||||
"""Convert GSM 03.38 encoded bytes to a string."""
|
"""Convert GSM 03.38 encoded bytes to a string."""
|
||||||
|
|
||||||
@@ -71,6 +81,7 @@ class GsmStringAdapter(Adapter):
|
|||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
return obj.encode(self.codec, self.err)
|
return obj.encode(self.codec, self.err)
|
||||||
|
|
||||||
|
|
||||||
def filter_dict(d, exclude_prefix='_'):
|
def filter_dict(d, exclude_prefix='_'):
|
||||||
"""filter the input dict to ensure no keys starting with 'exclude_prefix' remain."""
|
"""filter the input dict to ensure no keys starting with 'exclude_prefix' remain."""
|
||||||
if not isinstance(d, dict):
|
if not isinstance(d, dict):
|
||||||
@@ -85,8 +96,6 @@ def filter_dict(d, exclude_prefix='_'):
|
|||||||
res[key] = value
|
res[key] = value
|
||||||
return res
|
return res
|
||||||
|
|
||||||
from construct.lib.containers import Container, ListContainer
|
|
||||||
from construct.core import EnumIntegerString
|
|
||||||
|
|
||||||
def normalize_construct(c):
|
def normalize_construct(c):
|
||||||
"""Convert a construct specific type to a related base type, mostly useful
|
"""Convert a construct specific type to a related base type, mostly useful
|
||||||
@@ -106,6 +115,7 @@ def normalize_construct(c):
|
|||||||
r = c
|
r = c
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def parse_construct(c, raw_bin_data: bytes, length: typing.Optional[int] = None, exclude_prefix: str = '_'):
|
def parse_construct(c, raw_bin_data: bytes, length: typing.Optional[int] = None, exclude_prefix: str = '_'):
|
||||||
"""Helper function to wrap around normalize_construct() and filter_dict()."""
|
"""Helper function to wrap around normalize_construct() and filter_dict()."""
|
||||||
if not length:
|
if not length:
|
||||||
@@ -113,6 +123,7 @@ def parse_construct(c, raw_bin_data:bytes, length:typing.Optional[int]=None, exc
|
|||||||
parsed = c.parse(raw_bin_data, total_len=length)
|
parsed = c.parse(raw_bin_data, total_len=length)
|
||||||
return normalize_construct(parsed)
|
return normalize_construct(parsed)
|
||||||
|
|
||||||
|
|
||||||
# here we collect some shared / common definitions of data types
|
# here we collect some shared / common definitions of data types
|
||||||
LV = Prefixed(Int8ub, HexAdapter(GreedyBytes))
|
LV = Prefixed(Int8ub, HexAdapter(GreedyBytes))
|
||||||
|
|
||||||
@@ -129,6 +140,7 @@ ByteRFU = Default(Byte, __RFU_VALUE)
|
|||||||
# Field that packs all remaining Reserved for Future Use (RFU) bytes
|
# Field that packs all remaining Reserved for Future Use (RFU) bytes
|
||||||
GreedyBytesRFU = Default(GreedyBytes, b'')
|
GreedyBytesRFU = Default(GreedyBytes, b'')
|
||||||
|
|
||||||
|
|
||||||
def BitsRFU(n=1):
|
def BitsRFU(n=1):
|
||||||
'''
|
'''
|
||||||
Field that packs Reserved for Future Use (RFU) bit(s)
|
Field that packs Reserved for Future Use (RFU) bit(s)
|
||||||
@@ -143,6 +155,7 @@ def BitsRFU(n=1):
|
|||||||
'''
|
'''
|
||||||
return Default(BitsInteger(n), __RFU_VALUE)
|
return Default(BitsInteger(n), __RFU_VALUE)
|
||||||
|
|
||||||
|
|
||||||
def BytesRFU(n=1):
|
def BytesRFU(n=1):
|
||||||
'''
|
'''
|
||||||
Field that packs Reserved for Future Use (RFU) byte(s)
|
Field that packs Reserved for Future Use (RFU) byte(s)
|
||||||
@@ -157,6 +170,7 @@ def BytesRFU(n=1):
|
|||||||
'''
|
'''
|
||||||
return Default(Bytes(n), __RFU_VALUE)
|
return Default(Bytes(n), __RFU_VALUE)
|
||||||
|
|
||||||
|
|
||||||
def GsmString(n):
|
def GsmString(n):
|
||||||
'''
|
'''
|
||||||
GSM 03.38 encoded byte string of fixed length n.
|
GSM 03.38 encoded byte string of fixed length n.
|
||||||
|
|||||||
@@ -21,21 +21,26 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class NoCardError(Exception):
|
class NoCardError(Exception):
|
||||||
"""No card was found in the reader."""
|
"""No card was found in the reader."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ProtocolError(Exception):
|
class ProtocolError(Exception):
|
||||||
"""Some kind of protocol level error interfacing with the card."""
|
"""Some kind of protocol level error interfacing with the card."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ReaderError(Exception):
|
class ReaderError(Exception):
|
||||||
"""Some kind of general error with the card reader."""
|
"""Some kind of general error with the card reader."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SwMatchError(Exception):
|
class SwMatchError(Exception):
|
||||||
"""Raised when an operation specifies an expected SW but the actual SW from
|
"""Raised when an operation specifies an expected SW but the actual SW from
|
||||||
the card doesn't match."""
|
the card doesn't match."""
|
||||||
|
|
||||||
def __init__(self, sw_actual: str, sw_expected: str, rs=None):
|
def __init__(self, sw_actual: str, sw_expected: str, rs=None):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@@ -46,6 +51,7 @@ class SwMatchError(Exception):
|
|||||||
self.sw_actual = sw_actual
|
self.sw_actual = sw_actual
|
||||||
self.sw_expected = sw_expected
|
self.sw_expected = sw_expected
|
||||||
self.rs = rs
|
self.rs = rs
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.rs:
|
if self.rs:
|
||||||
r = self.rs.interpret_sw(self.sw_actual)
|
r = self.rs.interpret_sw(self.sw_actual)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ from pySim.exceptions import *
|
|||||||
from pySim.jsonpath import js_path_find, js_path_modify
|
from pySim.jsonpath import js_path_find, js_path_modify
|
||||||
from pySim.commands import SimCardCommands
|
from pySim.commands import SimCardCommands
|
||||||
|
|
||||||
|
|
||||||
class CardFile(object):
|
class CardFile(object):
|
||||||
"""Base class for all objects in the smart card filesystem.
|
"""Base class for all objects in the smart card filesystem.
|
||||||
Serve as a common ancestor to all other file types; rarely used directly.
|
Serve as a common ancestor to all other file types; rarely used directly.
|
||||||
@@ -206,6 +207,7 @@ class CardFile(object):
|
|||||||
return self.parent.get_profile()
|
return self.parent.get_profile()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class CardDF(CardFile):
|
class CardDF(CardFile):
|
||||||
"""DF (Dedicated File) in the smart card filesystem. Those are basically sub-directories."""
|
"""DF (Dedicated File) in the smart card filesystem. Those are basically sub-directories."""
|
||||||
|
|
||||||
@@ -242,13 +244,16 @@ class CardDF(CardFile):
|
|||||||
if child.fid in self.children:
|
if child.fid in self.children:
|
||||||
if ignore_existing:
|
if ignore_existing:
|
||||||
return
|
return
|
||||||
raise ValueError("File with given fid %s already exists in %s" % (child.fid, self))
|
raise ValueError(
|
||||||
|
"File with given fid %s already exists in %s" % (child.fid, self))
|
||||||
if self.lookup_file_by_sfid(child.sfid):
|
if self.lookup_file_by_sfid(child.sfid):
|
||||||
raise ValueError("File with given sfid %s already exists in %s" % (child.sfid, self))
|
raise ValueError(
|
||||||
|
"File with given sfid %s already exists in %s" % (child.sfid, self))
|
||||||
if self.lookup_file_by_name(child.name):
|
if self.lookup_file_by_name(child.name):
|
||||||
if ignore_existing:
|
if ignore_existing:
|
||||||
return
|
return
|
||||||
raise ValueError("File with given name %s already exists in %s" % (child.name, self))
|
raise ValueError(
|
||||||
|
"File with given name %s already exists in %s" % (child.name, self))
|
||||||
self.children[child.fid] = child
|
self.children[child.fid] = child
|
||||||
child.parent = self
|
child.parent = self
|
||||||
|
|
||||||
@@ -307,6 +312,7 @@ class CardDF(CardFile):
|
|||||||
|
|
||||||
class CardMF(CardDF):
|
class CardMF(CardDF):
|
||||||
"""MF (Master File) in the smart card filesystem"""
|
"""MF (Master File) in the smart card filesystem"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
# can be overridden; use setdefault
|
# can be overridden; use setdefault
|
||||||
kwargs.setdefault('fid', '3f00')
|
kwargs.setdefault('fid', '3f00')
|
||||||
@@ -353,7 +359,8 @@ class CardMF(CardDF):
|
|||||||
if flags == [] or 'AIDS' in flags:
|
if flags == [] or 'AIDS' in flags:
|
||||||
sels.update({x.aid: x for x in self.applications.values()})
|
sels.update({x.aid: x for x in self.applications.values()})
|
||||||
if flags == [] or 'ANAMES' in flags:
|
if flags == [] or 'ANAMES' in flags:
|
||||||
sels.update({x.name: x for x in self.applications.values() if x.name})
|
sels.update(
|
||||||
|
{x.name: x for x in self.applications.values() if x.name})
|
||||||
return sels
|
return sels
|
||||||
|
|
||||||
def decode_select_response(self, data_hex: str) -> object:
|
def decode_select_response(self, data_hex: str) -> object:
|
||||||
@@ -372,8 +379,10 @@ class CardMF(CardDF):
|
|||||||
else:
|
else:
|
||||||
return data_hex
|
return data_hex
|
||||||
|
|
||||||
|
|
||||||
class CardADF(CardDF):
|
class CardADF(CardDF):
|
||||||
"""ADF (Application Dedicated File) in the smart card filesystem"""
|
"""ADF (Application Dedicated File) in the smart card filesystem"""
|
||||||
|
|
||||||
def __init__(self, aid: str, **kwargs):
|
def __init__(self, aid: str, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
# reference to CardApplication may be set from CardApplication constructor
|
# reference to CardApplication may be set from CardApplication constructor
|
||||||
@@ -395,6 +404,7 @@ class CardADF(CardDF):
|
|||||||
|
|
||||||
class CardEF(CardFile):
|
class CardEF(CardFile):
|
||||||
"""EF (Entry File) in the smart card filesystem"""
|
"""EF (Entry File) in the smart card filesystem"""
|
||||||
|
|
||||||
def __init__(self, *, fid, **kwargs):
|
def __init__(self, *, fid, **kwargs):
|
||||||
kwargs['fid'] = fid
|
kwargs['fid'] = fid
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
@@ -414,7 +424,8 @@ class CardEF(CardFile):
|
|||||||
"""
|
"""
|
||||||
# global selectable names + those of the parent DF
|
# global selectable names + those of the parent DF
|
||||||
sels = super().get_selectables(flags)
|
sels = super().get_selectables(flags)
|
||||||
sels.update({x.name:x for x in self.parent.children.values() if x != self})
|
sels.update(
|
||||||
|
{x.name: x for x in self.parent.children.values() if x != self})
|
||||||
return sels
|
return sels
|
||||||
|
|
||||||
|
|
||||||
@@ -427,12 +438,16 @@ class TransparentEF(CardEF):
|
|||||||
@with_default_category('Transparent EF Commands')
|
@with_default_category('Transparent EF Commands')
|
||||||
class ShellCommands(CommandSet):
|
class ShellCommands(CommandSet):
|
||||||
"""Shell commands specific for transparent EFs."""
|
"""Shell commands specific for transparent EFs."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
read_bin_parser = argparse.ArgumentParser()
|
read_bin_parser = argparse.ArgumentParser()
|
||||||
read_bin_parser.add_argument('--offset', type=int, default=0, help='Byte offset for start of read')
|
read_bin_parser.add_argument(
|
||||||
read_bin_parser.add_argument('--length', type=int, help='Number of bytes to read')
|
'--offset', type=int, default=0, help='Byte offset for start of read')
|
||||||
|
read_bin_parser.add_argument(
|
||||||
|
'--length', type=int, help='Number of bytes to read')
|
||||||
|
|
||||||
@cmd2.with_argparser(read_bin_parser)
|
@cmd2.with_argparser(read_bin_parser)
|
||||||
def do_read_binary(self, opts):
|
def do_read_binary(self, opts):
|
||||||
"""Read binary data from a transparent EF"""
|
"""Read binary data from a transparent EF"""
|
||||||
@@ -442,6 +457,7 @@ class TransparentEF(CardEF):
|
|||||||
read_bin_dec_parser = argparse.ArgumentParser()
|
read_bin_dec_parser = argparse.ArgumentParser()
|
||||||
read_bin_dec_parser.add_argument('--oneline', action='store_true',
|
read_bin_dec_parser.add_argument('--oneline', action='store_true',
|
||||||
help='No JSON pretty-printing, dump as a single line')
|
help='No JSON pretty-printing, dump as a single line')
|
||||||
|
|
||||||
@cmd2.with_argparser(read_bin_dec_parser)
|
@cmd2.with_argparser(read_bin_dec_parser)
|
||||||
def do_read_binary_decoded(self, opts):
|
def do_read_binary_decoded(self, opts):
|
||||||
"""Read + decode data from a transparent EF"""
|
"""Read + decode data from a transparent EF"""
|
||||||
@@ -449,8 +465,11 @@ class TransparentEF(CardEF):
|
|||||||
self._cmd.poutput_json(data, opts.oneline)
|
self._cmd.poutput_json(data, opts.oneline)
|
||||||
|
|
||||||
upd_bin_parser = argparse.ArgumentParser()
|
upd_bin_parser = argparse.ArgumentParser()
|
||||||
upd_bin_parser.add_argument('--offset', type=int, default=0, help='Byte offset for start of read')
|
upd_bin_parser.add_argument(
|
||||||
upd_bin_parser.add_argument('data', help='Data bytes (hex format) to write')
|
'--offset', type=int, default=0, help='Byte offset for start of read')
|
||||||
|
upd_bin_parser.add_argument(
|
||||||
|
'data', help='Data bytes (hex format) to write')
|
||||||
|
|
||||||
@cmd2.with_argparser(upd_bin_parser)
|
@cmd2.with_argparser(upd_bin_parser)
|
||||||
def do_update_binary(self, opts):
|
def do_update_binary(self, opts):
|
||||||
"""Update (Write) data of a transparent EF"""
|
"""Update (Write) data of a transparent EF"""
|
||||||
@@ -459,15 +478,18 @@ class TransparentEF(CardEF):
|
|||||||
self._cmd.poutput(data)
|
self._cmd.poutput(data)
|
||||||
|
|
||||||
upd_bin_dec_parser = argparse.ArgumentParser()
|
upd_bin_dec_parser = argparse.ArgumentParser()
|
||||||
upd_bin_dec_parser.add_argument('data', help='Abstract data (JSON format) to write')
|
upd_bin_dec_parser.add_argument(
|
||||||
|
'data', help='Abstract data (JSON format) to write')
|
||||||
upd_bin_dec_parser.add_argument('--json-path', type=str,
|
upd_bin_dec_parser.add_argument('--json-path', type=str,
|
||||||
help='JSON path to modify specific element of file only')
|
help='JSON path to modify specific element of file only')
|
||||||
|
|
||||||
@cmd2.with_argparser(upd_bin_dec_parser)
|
@cmd2.with_argparser(upd_bin_dec_parser)
|
||||||
def do_update_binary_decoded(self, opts):
|
def do_update_binary_decoded(self, opts):
|
||||||
"""Encode + Update (Write) data of a transparent EF"""
|
"""Encode + Update (Write) data of a transparent EF"""
|
||||||
if opts.json_path:
|
if opts.json_path:
|
||||||
(data_json, sw) = self._cmd.rs.read_binary_dec()
|
(data_json, sw) = self._cmd.rs.read_binary_dec()
|
||||||
js_path_modify(data_json, opts.json_path, json.loads(opts.data))
|
js_path_modify(data_json, opts.json_path,
|
||||||
|
json.loads(opts.data))
|
||||||
else:
|
else:
|
||||||
data_json = json.loads(opts.data)
|
data_json = json.loads(opts.data)
|
||||||
(data, sw) = self._cmd.rs.update_binary_dec(data_json)
|
(data, sw) = self._cmd.rs.update_binary_dec(data_json)
|
||||||
@@ -493,7 +515,6 @@ class TransparentEF(CardEF):
|
|||||||
if data:
|
if data:
|
||||||
self._cmd.poutput_json(data)
|
self._cmd.poutput_json(data)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
|
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
|
||||||
size={1, None}):
|
size={1, None}):
|
||||||
"""
|
"""
|
||||||
@@ -588,7 +609,8 @@ class TransparentEF(CardEF):
|
|||||||
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
||||||
t.from_dict(abstract_data)
|
t.from_dict(abstract_data)
|
||||||
return t.to_tlv()
|
return t.to_tlv()
|
||||||
raise NotImplementedError("%s encoder not yet implemented. Patches welcome." % self)
|
raise NotImplementedError(
|
||||||
|
"%s encoder not yet implemented. Patches welcome." % self)
|
||||||
|
|
||||||
def encode_hex(self, abstract_data: dict) -> str:
|
def encode_hex(self, abstract_data: dict) -> str:
|
||||||
"""Encode abstract representation into raw (hex string) data.
|
"""Encode abstract representation into raw (hex string) data.
|
||||||
@@ -615,7 +637,8 @@ class TransparentEF(CardEF):
|
|||||||
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
||||||
t.from_dict(abstract_data)
|
t.from_dict(abstract_data)
|
||||||
return b2h(t.to_tlv())
|
return b2h(t.to_tlv())
|
||||||
raise NotImplementedError("%s encoder not yet implemented. Patches welcome." % self)
|
raise NotImplementedError(
|
||||||
|
"%s encoder not yet implemented. Patches welcome." % self)
|
||||||
|
|
||||||
|
|
||||||
class LinFixedEF(CardEF):
|
class LinFixedEF(CardEF):
|
||||||
@@ -627,12 +650,16 @@ class LinFixedEF(CardEF):
|
|||||||
@with_default_category('Linear Fixed EF Commands')
|
@with_default_category('Linear Fixed EF Commands')
|
||||||
class ShellCommands(CommandSet):
|
class ShellCommands(CommandSet):
|
||||||
"""Shell commands specific for Linear Fixed EFs."""
|
"""Shell commands specific for Linear Fixed EFs."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
read_rec_parser = argparse.ArgumentParser()
|
read_rec_parser = argparse.ArgumentParser()
|
||||||
read_rec_parser.add_argument('record_nr', type=int, help='Number of record to be read')
|
read_rec_parser.add_argument(
|
||||||
read_rec_parser.add_argument('--count', type=int, default=1, help='Number of records to be read, beginning at record_nr')
|
'record_nr', type=int, help='Number of record to be read')
|
||||||
|
read_rec_parser.add_argument(
|
||||||
|
'--count', type=int, default=1, help='Number of records to be read, beginning at record_nr')
|
||||||
|
|
||||||
@cmd2.with_argparser(read_rec_parser)
|
@cmd2.with_argparser(read_rec_parser)
|
||||||
def do_read_record(self, opts):
|
def do_read_record(self, opts):
|
||||||
"""Read one or multiple records from a record-oriented EF"""
|
"""Read one or multiple records from a record-oriented EF"""
|
||||||
@@ -646,9 +673,11 @@ class LinFixedEF(CardEF):
|
|||||||
self._cmd.poutput("%03d %s" % (recnr, recstr))
|
self._cmd.poutput("%03d %s" % (recnr, recstr))
|
||||||
|
|
||||||
read_rec_dec_parser = argparse.ArgumentParser()
|
read_rec_dec_parser = argparse.ArgumentParser()
|
||||||
read_rec_dec_parser.add_argument('record_nr', type=int, help='Number of record to be read')
|
read_rec_dec_parser.add_argument(
|
||||||
|
'record_nr', type=int, help='Number of record to be read')
|
||||||
read_rec_dec_parser.add_argument('--oneline', action='store_true',
|
read_rec_dec_parser.add_argument('--oneline', action='store_true',
|
||||||
help='No JSON pretty-printing, dump as a single line')
|
help='No JSON pretty-printing, dump as a single line')
|
||||||
|
|
||||||
@cmd2.with_argparser(read_rec_dec_parser)
|
@cmd2.with_argparser(read_rec_dec_parser)
|
||||||
def do_read_record_decoded(self, opts):
|
def do_read_record_decoded(self, opts):
|
||||||
"""Read + decode a record from a record-oriented EF"""
|
"""Read + decode a record from a record-oriented EF"""
|
||||||
@@ -656,6 +685,7 @@ class LinFixedEF(CardEF):
|
|||||||
self._cmd.poutput_json(data, opts.oneline)
|
self._cmd.poutput_json(data, opts.oneline)
|
||||||
|
|
||||||
read_recs_parser = argparse.ArgumentParser()
|
read_recs_parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
@cmd2.with_argparser(read_recs_parser)
|
@cmd2.with_argparser(read_recs_parser)
|
||||||
def do_read_records(self, opts):
|
def do_read_records(self, opts):
|
||||||
"""Read all records from a record-oriented EF"""
|
"""Read all records from a record-oriented EF"""
|
||||||
@@ -671,6 +701,7 @@ class LinFixedEF(CardEF):
|
|||||||
read_recs_dec_parser = argparse.ArgumentParser()
|
read_recs_dec_parser = argparse.ArgumentParser()
|
||||||
read_recs_dec_parser.add_argument('--oneline', action='store_true',
|
read_recs_dec_parser.add_argument('--oneline', action='store_true',
|
||||||
help='No JSON pretty-printing, dump as a single line')
|
help='No JSON pretty-printing, dump as a single line')
|
||||||
|
|
||||||
@cmd2.with_argparser(read_recs_dec_parser)
|
@cmd2.with_argparser(read_recs_dec_parser)
|
||||||
def do_read_records_decoded(self, opts):
|
def do_read_records_decoded(self, opts):
|
||||||
"""Read + decode all records from a record-oriented EF"""
|
"""Read + decode all records from a record-oriented EF"""
|
||||||
@@ -683,8 +714,11 @@ class LinFixedEF(CardEF):
|
|||||||
self._cmd.poutput_json(data_list, opts.oneline)
|
self._cmd.poutput_json(data_list, opts.oneline)
|
||||||
|
|
||||||
upd_rec_parser = argparse.ArgumentParser()
|
upd_rec_parser = argparse.ArgumentParser()
|
||||||
upd_rec_parser.add_argument('record_nr', type=int, help='Number of record to be read')
|
upd_rec_parser.add_argument(
|
||||||
upd_rec_parser.add_argument('data', help='Data bytes (hex format) to write')
|
'record_nr', type=int, help='Number of record to be read')
|
||||||
|
upd_rec_parser.add_argument(
|
||||||
|
'data', help='Data bytes (hex format) to write')
|
||||||
|
|
||||||
@cmd2.with_argparser(upd_rec_parser)
|
@cmd2.with_argparser(upd_rec_parser)
|
||||||
def do_update_record(self, opts):
|
def do_update_record(self, opts):
|
||||||
"""Update (write) data to a record-oriented EF"""
|
"""Update (write) data to a record-oriented EF"""
|
||||||
@@ -693,24 +727,31 @@ class LinFixedEF(CardEF):
|
|||||||
self._cmd.poutput(data)
|
self._cmd.poutput(data)
|
||||||
|
|
||||||
upd_rec_dec_parser = argparse.ArgumentParser()
|
upd_rec_dec_parser = argparse.ArgumentParser()
|
||||||
upd_rec_dec_parser.add_argument('record_nr', type=int, help='Number of record to be read')
|
upd_rec_dec_parser.add_argument(
|
||||||
upd_rec_dec_parser.add_argument('data', help='Abstract data (JSON format) to write')
|
'record_nr', type=int, help='Number of record to be read')
|
||||||
|
upd_rec_dec_parser.add_argument(
|
||||||
|
'data', help='Abstract data (JSON format) to write')
|
||||||
upd_rec_dec_parser.add_argument('--json-path', type=str,
|
upd_rec_dec_parser.add_argument('--json-path', type=str,
|
||||||
help='JSON path to modify specific element of record only')
|
help='JSON path to modify specific element of record only')
|
||||||
|
|
||||||
@cmd2.with_argparser(upd_rec_dec_parser)
|
@cmd2.with_argparser(upd_rec_dec_parser)
|
||||||
def do_update_record_decoded(self, opts):
|
def do_update_record_decoded(self, opts):
|
||||||
"""Encode + Update (write) data to a record-oriented EF"""
|
"""Encode + Update (write) data to a record-oriented EF"""
|
||||||
if opts.json_path:
|
if opts.json_path:
|
||||||
(data_json, sw) = self._cmd.rs.read_record_dec(opts.record_nr)
|
(data_json, sw) = self._cmd.rs.read_record_dec(opts.record_nr)
|
||||||
js_path_modify(data_json, opts.json_path, json.loads(opts.data))
|
js_path_modify(data_json, opts.json_path,
|
||||||
|
json.loads(opts.data))
|
||||||
else:
|
else:
|
||||||
data_json = json.loads(opts.data)
|
data_json = json.loads(opts.data)
|
||||||
(data, sw) = self._cmd.rs.update_record_dec(opts.record_nr, data_json)
|
(data, sw) = self._cmd.rs.update_record_dec(
|
||||||
|
opts.record_nr, data_json)
|
||||||
if data:
|
if data:
|
||||||
self._cmd.poutput(data)
|
self._cmd.poutput(data)
|
||||||
|
|
||||||
edit_rec_dec_parser = argparse.ArgumentParser()
|
edit_rec_dec_parser = argparse.ArgumentParser()
|
||||||
edit_rec_dec_parser.add_argument('record_nr', type=int, help='Number of record to be edited')
|
edit_rec_dec_parser.add_argument(
|
||||||
|
'record_nr', type=int, help='Number of record to be edited')
|
||||||
|
|
||||||
@cmd2.with_argparser(edit_rec_dec_parser)
|
@cmd2.with_argparser(edit_rec_dec_parser)
|
||||||
def do_edit_record_decoded(self, opts):
|
def do_edit_record_decoded(self, opts):
|
||||||
"""Edit the JSON representation of one record in an editor."""
|
"""Edit the JSON representation of one record in an editor."""
|
||||||
@@ -727,11 +768,11 @@ class LinFixedEF(CardEF):
|
|||||||
if edited_json == orig_json:
|
if edited_json == orig_json:
|
||||||
self._cmd.poutput("Data not modified, skipping write")
|
self._cmd.poutput("Data not modified, skipping write")
|
||||||
else:
|
else:
|
||||||
(data, sw) = self._cmd.rs.update_record_dec(opts.record_nr, edited_json)
|
(data, sw) = self._cmd.rs.update_record_dec(
|
||||||
|
opts.record_nr, edited_json)
|
||||||
if data:
|
if data:
|
||||||
self._cmd.poutput_json(data)
|
self._cmd.poutput_json(data)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None,
|
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None,
|
||||||
parent: Optional[CardDF] = None, rec_len={1, None}):
|
parent: Optional[CardDF] = None, rec_len={1, None}):
|
||||||
"""
|
"""
|
||||||
@@ -828,7 +869,8 @@ class LinFixedEF(CardEF):
|
|||||||
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
||||||
t.from_dict(abstract_data)
|
t.from_dict(abstract_data)
|
||||||
return b2h(t.to_tlv())
|
return b2h(t.to_tlv())
|
||||||
raise NotImplementedError("%s encoder not yet implemented. Patches welcome." % self)
|
raise NotImplementedError(
|
||||||
|
"%s encoder not yet implemented. Patches welcome." % self)
|
||||||
|
|
||||||
def encode_record_bin(self, abstract_data: dict) -> bytearray:
|
def encode_record_bin(self, abstract_data: dict) -> bytearray:
|
||||||
"""Encode abstract representation into raw (binary) data.
|
"""Encode abstract representation into raw (binary) data.
|
||||||
@@ -854,14 +896,19 @@ class LinFixedEF(CardEF):
|
|||||||
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
||||||
t.from_dict(abstract_data)
|
t.from_dict(abstract_data)
|
||||||
return t.to_tlv()
|
return t.to_tlv()
|
||||||
raise NotImplementedError("%s encoder not yet implemented. Patches welcome." % self)
|
raise NotImplementedError(
|
||||||
|
"%s encoder not yet implemented. Patches welcome." % self)
|
||||||
|
|
||||||
|
|
||||||
class CyclicEF(LinFixedEF):
|
class CyclicEF(LinFixedEF):
|
||||||
"""Cyclic EF (Entry File) in the smart card filesystem"""
|
"""Cyclic EF (Entry File) in the smart card filesystem"""
|
||||||
# we don't really have any special support for those; just recycling LinFixedEF here
|
# we don't really have any special support for those; just recycling LinFixedEF here
|
||||||
|
|
||||||
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
|
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
|
||||||
rec_len={1, None}):
|
rec_len={1, None}):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, rec_len=rec_len)
|
super().__init__(fid=fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, parent=parent, rec_len=rec_len)
|
||||||
|
|
||||||
|
|
||||||
class TransRecEF(TransparentEF):
|
class TransRecEF(TransparentEF):
|
||||||
"""Transparent EF (Entry File) containing fixed-size records.
|
"""Transparent EF (Entry File) containing fixed-size records.
|
||||||
@@ -872,6 +919,7 @@ class TransRecEF(TransparentEF):
|
|||||||
We add a special class for those, so the user only has to provide encoder/decoder functions
|
We add a special class for those, so the user only has to provide encoder/decoder functions
|
||||||
for a record, while this class takes care of split / merge of records.
|
for a record, while this class takes care of split / merge of records.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, fid: str, rec_len: int, sfid: str = None, name: str = None, desc: str = None,
|
def __init__(self, fid: str, rec_len: int, sfid: str = None, name: str = None, desc: str = None,
|
||||||
parent: Optional[CardDF] = None, size={1, None}):
|
parent: Optional[CardDF] = None, size={1, None}):
|
||||||
"""
|
"""
|
||||||
@@ -965,7 +1013,8 @@ class TransRecEF(TransparentEF):
|
|||||||
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
||||||
t.from_dict(abstract_data)
|
t.from_dict(abstract_data)
|
||||||
return b2h(t.to_tlv())
|
return b2h(t.to_tlv())
|
||||||
raise NotImplementedError("%s encoder not yet implemented. Patches welcome." % self)
|
raise NotImplementedError(
|
||||||
|
"%s encoder not yet implemented. Patches welcome." % self)
|
||||||
|
|
||||||
def encode_record_bin(self, abstract_data: dict) -> bytearray:
|
def encode_record_bin(self, abstract_data: dict) -> bytearray:
|
||||||
"""Encode abstract representation into raw (binary) data.
|
"""Encode abstract representation into raw (binary) data.
|
||||||
@@ -991,10 +1040,12 @@ class TransRecEF(TransparentEF):
|
|||||||
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
t = self._tlv() if inspect.isclass(self._tlv) else self._tlv
|
||||||
t.from_dict(abstract_data)
|
t.from_dict(abstract_data)
|
||||||
return t.to_tlv()
|
return t.to_tlv()
|
||||||
raise NotImplementedError("%s encoder not yet implemented. Patches welcome." % self)
|
raise NotImplementedError(
|
||||||
|
"%s encoder not yet implemented. Patches welcome." % self)
|
||||||
|
|
||||||
def _decode_bin(self, raw_bin_data: bytearray):
|
def _decode_bin(self, raw_bin_data: bytearray):
|
||||||
chunks = [raw_bin_data[i:i+self.rec_len] for i in range(0, len(raw_bin_data), self.rec_len)]
|
chunks = [raw_bin_data[i:i+self.rec_len]
|
||||||
|
for i in range(0, len(raw_bin_data), self.rec_len)]
|
||||||
return [self.decode_record_bin(x) for x in chunks]
|
return [self.decode_record_bin(x) for x in chunks]
|
||||||
|
|
||||||
def _encode_bin(self, abstract_data) -> bytes:
|
def _encode_bin(self, abstract_data) -> bytes:
|
||||||
@@ -1014,11 +1065,14 @@ class BerTlvEF(CardEF):
|
|||||||
@with_default_category('BER-TLV EF Commands')
|
@with_default_category('BER-TLV EF Commands')
|
||||||
class ShellCommands(CommandSet):
|
class ShellCommands(CommandSet):
|
||||||
"""Shell commands specific for BER-TLV EFs."""
|
"""Shell commands specific for BER-TLV EFs."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
retrieve_data_parser = argparse.ArgumentParser()
|
retrieve_data_parser = argparse.ArgumentParser()
|
||||||
retrieve_data_parser.add_argument('tag', type=auto_int, help='BER-TLV Tag of value to retrieve')
|
retrieve_data_parser.add_argument(
|
||||||
|
'tag', type=auto_int, help='BER-TLV Tag of value to retrieve')
|
||||||
|
|
||||||
@cmd2.with_argparser(retrieve_data_parser)
|
@cmd2.with_argparser(retrieve_data_parser)
|
||||||
def do_retrieve_data(self, opts):
|
def do_retrieve_data(self, opts):
|
||||||
"""Retrieve (Read) data from a BER-TLV EF"""
|
"""Retrieve (Read) data from a BER-TLV EF"""
|
||||||
@@ -1031,8 +1085,11 @@ class BerTlvEF(CardEF):
|
|||||||
self._cmd.poutput(tags)
|
self._cmd.poutput(tags)
|
||||||
|
|
||||||
set_data_parser = argparse.ArgumentParser()
|
set_data_parser = argparse.ArgumentParser()
|
||||||
set_data_parser.add_argument('tag', type=auto_int, help='BER-TLV Tag of value to set')
|
set_data_parser.add_argument(
|
||||||
set_data_parser.add_argument('data', help='Data bytes (hex format) to write')
|
'tag', type=auto_int, help='BER-TLV Tag of value to set')
|
||||||
|
set_data_parser.add_argument(
|
||||||
|
'data', help='Data bytes (hex format) to write')
|
||||||
|
|
||||||
@cmd2.with_argparser(set_data_parser)
|
@cmd2.with_argparser(set_data_parser)
|
||||||
def do_set_data(self, opts):
|
def do_set_data(self, opts):
|
||||||
"""Set (Write) data for a given tag in a BER-TLV EF"""
|
"""Set (Write) data for a given tag in a BER-TLV EF"""
|
||||||
@@ -1041,7 +1098,9 @@ class BerTlvEF(CardEF):
|
|||||||
self._cmd.poutput(data)
|
self._cmd.poutput(data)
|
||||||
|
|
||||||
del_data_parser = argparse.ArgumentParser()
|
del_data_parser = argparse.ArgumentParser()
|
||||||
del_data_parser.add_argument('tag', type=auto_int, help='BER-TLV Tag of value to set')
|
del_data_parser.add_argument(
|
||||||
|
'tag', type=auto_int, help='BER-TLV Tag of value to set')
|
||||||
|
|
||||||
@cmd2.with_argparser(del_data_parser)
|
@cmd2.with_argparser(del_data_parser)
|
||||||
def do_delete_data(self, opts):
|
def do_delete_data(self, opts):
|
||||||
"""Delete data for a given tag in a BER-TLV EF"""
|
"""Delete data for a given tag in a BER-TLV EF"""
|
||||||
@@ -1049,7 +1108,6 @@ class BerTlvEF(CardEF):
|
|||||||
if data:
|
if data:
|
||||||
self._cmd.poutput(data)
|
self._cmd.poutput(data)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
|
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
|
||||||
size={1, None}):
|
size={1, None}):
|
||||||
"""
|
"""
|
||||||
@@ -1069,6 +1127,7 @@ class BerTlvEF(CardEF):
|
|||||||
|
|
||||||
class RuntimeState(object):
|
class RuntimeState(object):
|
||||||
"""Represent the runtime state of a session with a card."""
|
"""Represent the runtime state of a session with a card."""
|
||||||
|
|
||||||
def __init__(self, card, profile: 'CardProfile'):
|
def __init__(self, card, profile: 'CardProfile'):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@@ -1082,7 +1141,8 @@ class RuntimeState(object):
|
|||||||
|
|
||||||
# make sure the class and selection control bytes, which are specified
|
# make sure the class and selection control bytes, which are specified
|
||||||
# by the card profile are used
|
# by the card profile are used
|
||||||
self.card.set_apdu_parameter(cla=self.profile.cla, sel_ctrl=self.profile.sel_ctrl)
|
self.card.set_apdu_parameter(
|
||||||
|
cla=self.profile.cla, sel_ctrl=self.profile.sel_ctrl)
|
||||||
|
|
||||||
# add application ADFs + MF-files from profile
|
# add application ADFs + MF-files from profile
|
||||||
apps = self._match_applications()
|
apps = self._match_applications()
|
||||||
@@ -1195,7 +1255,8 @@ class RuntimeState(object):
|
|||||||
"""Blindly try to select a file and automatically add a matching file
|
"""Blindly try to select a file and automatically add a matching file
|
||||||
object if the file actually exists."""
|
object if the file actually exists."""
|
||||||
if not is_hex(fid, 4, 4):
|
if not is_hex(fid, 4, 4):
|
||||||
raise ValueError("Cannot select unknown file by name %s, only hexadecimal 4 digit FID is allowed" % fid)
|
raise ValueError(
|
||||||
|
"Cannot select unknown file by name %s, only hexadecimal 4 digit FID is allowed" % fid)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(data, sw) = self.card._scc.select_file(fid)
|
(data, sw) = self.card._scc.select_file(fid)
|
||||||
@@ -1207,12 +1268,15 @@ class RuntimeState(object):
|
|||||||
|
|
||||||
select_resp = self.selected_file.decode_select_response(data)
|
select_resp = self.selected_file.decode_select_response(data)
|
||||||
if (select_resp['file_descriptor']['file_type'] == 'df'):
|
if (select_resp['file_descriptor']['file_type'] == 'df'):
|
||||||
f = CardDF(fid=fid, sfid=None, name="DF." + str(fid).upper(), desc="dedicated file, manually added at runtime")
|
f = CardDF(fid=fid, sfid=None, name="DF." + str(fid).upper(),
|
||||||
|
desc="dedicated file, manually added at runtime")
|
||||||
else:
|
else:
|
||||||
if (select_resp['file_descriptor']['structure'] == 'transparent'):
|
if (select_resp['file_descriptor']['structure'] == 'transparent'):
|
||||||
f = TransparentEF(fid=fid, sfid=None, name="EF." + str(fid).upper(), desc="elementary file, manually added at runtime")
|
f = TransparentEF(fid=fid, sfid=None, name="EF." + str(fid).upper(),
|
||||||
|
desc="elementary file, manually added at runtime")
|
||||||
else:
|
else:
|
||||||
f = LinFixedEF(fid=fid, sfid=None, name="EF." + str(fid).upper(), desc="elementary file, manually added at runtime")
|
f = LinFixedEF(fid=fid, sfid=None, name="EF." + str(fid).upper(),
|
||||||
|
desc="elementary file, manually added at runtime")
|
||||||
|
|
||||||
self.selected_file.add_files([f])
|
self.selected_file.add_files([f])
|
||||||
self.selected_file = f
|
self.selected_file = f
|
||||||
@@ -1408,9 +1472,9 @@ class RuntimeState(object):
|
|||||||
cmd_app.unregister_command_set(c)
|
cmd_app.unregister_command_set(c)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FileData(object):
|
class FileData(object):
|
||||||
"""Represent the runtime, on-card data."""
|
"""Represent the runtime, on-card data."""
|
||||||
|
|
||||||
def __init__(self, fdesc):
|
def __init__(self, fdesc):
|
||||||
self.desc = fdesc
|
self.desc = fdesc
|
||||||
self.fcp = None
|
self.fcp = None
|
||||||
@@ -1435,9 +1499,11 @@ def interpret_sw(sw_data:dict, sw:str):
|
|||||||
return (class_str, descr)
|
return (class_str, descr)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class CardApplication(object):
|
class CardApplication(object):
|
||||||
"""A card application is represented by an ADF (with contained hierarchy) and optionally
|
"""A card application is represented by an ADF (with contained hierarchy) and optionally
|
||||||
some SW definitions."""
|
some SW definitions."""
|
||||||
|
|
||||||
def __init__(self, name, adf: Optional[CardADF] = None, aid: str = None, sw: dict = None):
|
def __init__(self, name, adf: Optional[CardADF] = None, aid: str = None, sw: dict = None):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import pySim.ts_51_011
|
|||||||
# DF.EIRENE (FFFIS for GSM-R SIM Cards)
|
# DF.EIRENE (FFFIS for GSM-R SIM Cards)
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
|
||||||
class FuncNTypeAdapter(Adapter):
|
class FuncNTypeAdapter(Adapter):
|
||||||
def _decode(self, obj, context, path):
|
def _decode(self, obj, context, path):
|
||||||
bcd = swap_nibbles(b2h(obj))
|
bcd = swap_nibbles(b2h(obj))
|
||||||
@@ -50,19 +51,24 @@ class FuncNTypeAdapter(Adapter):
|
|||||||
return {'functional_number': bcd[:-1],
|
return {'functional_number': bcd[:-1],
|
||||||
'presentation_of_only_this_fn': last_digit & 4,
|
'presentation_of_only_this_fn': last_digit & 4,
|
||||||
'permanent_fn': last_digit & 8}
|
'permanent_fn': last_digit & 8}
|
||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
return 'FIXME'
|
return 'FIXME'
|
||||||
|
|
||||||
|
|
||||||
class EF_FN(LinFixedEF):
|
class EF_FN(LinFixedEF):
|
||||||
"""Section 7.2"""
|
"""Section 7.2"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6ff1', sfid=None, name='EF.EN', desc='Functional numbers', rec_len={9,9})
|
super().__init__(fid='6ff1', sfid=None, name='EF.EN',
|
||||||
|
desc='Functional numbers', rec_len={9, 9})
|
||||||
self._construct = Struct('functional_number_and_type'/FuncNTypeAdapter(Bytes(8)),
|
self._construct = Struct('functional_number_and_type'/FuncNTypeAdapter(Bytes(8)),
|
||||||
'list_number'/Int8ub)
|
'list_number'/Int8ub)
|
||||||
|
|
||||||
|
|
||||||
class PlConfAdapter(Adapter):
|
class PlConfAdapter(Adapter):
|
||||||
"""Section 7.4.3"""
|
"""Section 7.4.3"""
|
||||||
|
|
||||||
def _decode(self, obj, context, path):
|
def _decode(self, obj, context, path):
|
||||||
num = int(obj) & 0x7
|
num = int(obj) & 0x7
|
||||||
if num == 0:
|
if num == 0:
|
||||||
@@ -77,6 +83,7 @@ class PlConfAdapter(Adapter):
|
|||||||
return 1
|
return 1
|
||||||
elif num == 5:
|
elif num == 5:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
if obj == 'None':
|
if obj == 'None':
|
||||||
return 0
|
return 0
|
||||||
@@ -92,8 +99,10 @@ class PlConfAdapter(Adapter):
|
|||||||
elif obj == 0:
|
elif obj == 0:
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
|
|
||||||
class PlCallAdapter(Adapter):
|
class PlCallAdapter(Adapter):
|
||||||
"""Section 7.4.12"""
|
"""Section 7.4.12"""
|
||||||
|
|
||||||
def _decode(self, obj, context, path):
|
def _decode(self, obj, context, path):
|
||||||
num = int(obj) & 0x7
|
num = int(obj) & 0x7
|
||||||
if num == 0:
|
if num == 0:
|
||||||
@@ -112,6 +121,7 @@ class PlCallAdapter(Adapter):
|
|||||||
return 'B'
|
return 'B'
|
||||||
elif num == 7:
|
elif num == 7:
|
||||||
return 'A'
|
return 'A'
|
||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
if obj == 'None':
|
if obj == 'None':
|
||||||
return 0
|
return 0
|
||||||
@@ -130,10 +140,14 @@ class PlCallAdapter(Adapter):
|
|||||||
elif obj == 'A':
|
elif obj == 'A':
|
||||||
return 7
|
return 7
|
||||||
|
|
||||||
NextTableType = Enum(Byte, decision=0xf0, predefined=0xf1, num_dial_digits=0xf2, ic=0xf3, empty=0xff)
|
|
||||||
|
NextTableType = Enum(Byte, decision=0xf0, predefined=0xf1,
|
||||||
|
num_dial_digits=0xf2, ic=0xf3, empty=0xff)
|
||||||
|
|
||||||
|
|
||||||
class EF_CallconfC(TransparentEF):
|
class EF_CallconfC(TransparentEF):
|
||||||
"""Section 7.3"""
|
"""Section 7.3"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6ff2', sfid=None, name='EF.CallconfC', size={24, 24},
|
super().__init__(fid='6ff2', sfid=None, name='EF.CallconfC', size={24, 24},
|
||||||
desc='Call Configuration of emergency calls Configuration')
|
desc='Call Configuration of emergency calls Configuration')
|
||||||
@@ -147,29 +161,39 @@ class EF_CallconfC(TransparentEF):
|
|||||||
'shunting_emergency_gid'/Int8ub,
|
'shunting_emergency_gid'/Int8ub,
|
||||||
'imei'/BcdAdapter(Bytes(8)))
|
'imei'/BcdAdapter(Bytes(8)))
|
||||||
|
|
||||||
|
|
||||||
class EF_CallconfI(LinFixedEF):
|
class EF_CallconfI(LinFixedEF):
|
||||||
"""Section 7.5"""
|
"""Section 7.5"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6ff3', sfid=None, name='EF.CallconfI', rec_len={21, 21},
|
super().__init__(fid='6ff3', sfid=None, name='EF.CallconfI', rec_len={21, 21},
|
||||||
desc='Call Configuration of emergency calls Information')
|
desc='Call Configuration of emergency calls Information')
|
||||||
self._construct = Struct('t_dur'/Int24ub,
|
self._construct = Struct('t_dur'/Int24ub,
|
||||||
't_relcalc'/Int32ub,
|
't_relcalc'/Int32ub,
|
||||||
'pl_call'/PlCallAdapter(Int8ub),
|
'pl_call'/PlCallAdapter(Int8ub),
|
||||||
'cause'/FlagsEnum(Int8ub, powered_off=1, radio_link_error=2, user_command=5),
|
'cause' /
|
||||||
|
FlagsEnum(Int8ub, powered_off=1,
|
||||||
|
radio_link_error=2, user_command=5),
|
||||||
'gcr'/BcdAdapter(Bytes(4)),
|
'gcr'/BcdAdapter(Bytes(4)),
|
||||||
'fnr'/BcdAdapter(Bytes(8)))
|
'fnr'/BcdAdapter(Bytes(8)))
|
||||||
|
|
||||||
|
|
||||||
class EF_Shunting(TransparentEF):
|
class EF_Shunting(TransparentEF):
|
||||||
"""Section 7.6"""
|
"""Section 7.6"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6ff4', sfid=None, name='EF.Shunting', desc='Shunting', size={8,8})
|
super().__init__(fid='6ff4', sfid=None,
|
||||||
|
name='EF.Shunting', desc='Shunting', size={8, 8})
|
||||||
self._construct = Struct('common_gid'/Int8ub,
|
self._construct = Struct('common_gid'/Int8ub,
|
||||||
'shunting_gid'/Bytes(7))
|
'shunting_gid'/Bytes(7))
|
||||||
|
|
||||||
|
|
||||||
class EF_GsmrPLMN(LinFixedEF):
|
class EF_GsmrPLMN(LinFixedEF):
|
||||||
"""Section 7.7"""
|
"""Section 7.7"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6ff5', sfid=None, name='EF.GsmrPLMN', desc='GSM-R network selection', rec_len={9,9})
|
super().__init__(fid='6ff5', sfid=None, name='EF.GsmrPLMN',
|
||||||
|
desc='GSM-R network selection', rec_len={9, 9})
|
||||||
self._construct = Struct('plmn'/BcdAdapter(Bytes(3)),
|
self._construct = Struct('plmn'/BcdAdapter(Bytes(3)),
|
||||||
'class_of_network'/BitStruct('supported'/FlagsEnum(BitsInteger(5), vbs=1, vgcs=2, emlpp=4, fn=8, eirene=16),
|
'class_of_network'/BitStruct('supported'/FlagsEnum(BitsInteger(5), vbs=1, vgcs=2, emlpp=4, fn=8, eirene=16),
|
||||||
'preference'/BitsInteger(3)),
|
'preference'/BitsInteger(3)),
|
||||||
@@ -177,34 +201,46 @@ class EF_GsmrPLMN(LinFixedEF):
|
|||||||
'outgoing_ref_tbl'/HexAdapter(Bytes(2)),
|
'outgoing_ref_tbl'/HexAdapter(Bytes(2)),
|
||||||
'ic_table_ref'/HexAdapter(Bytes(1)))
|
'ic_table_ref'/HexAdapter(Bytes(1)))
|
||||||
|
|
||||||
|
|
||||||
class EF_IC(LinFixedEF):
|
class EF_IC(LinFixedEF):
|
||||||
"""Section 7.8"""
|
"""Section 7.8"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6f8d', sfid=None, name='EF.IC', desc='International Code', rec_len={7,7})
|
super().__init__(fid='6f8d', sfid=None, name='EF.IC',
|
||||||
|
desc='International Code', rec_len={7, 7})
|
||||||
self._construct = Struct('next_table_type'/NextTableType,
|
self._construct = Struct('next_table_type'/NextTableType,
|
||||||
'id_of_next_table'/HexAdapter(Bytes(2)),
|
'id_of_next_table'/HexAdapter(Bytes(2)),
|
||||||
'ic_decision_value'/BcdAdapter(Bytes(2)),
|
'ic_decision_value'/BcdAdapter(Bytes(2)),
|
||||||
'network_string_table_index'/Int8ub)
|
'network_string_table_index'/Int8ub)
|
||||||
|
|
||||||
|
|
||||||
class EF_NW(LinFixedEF):
|
class EF_NW(LinFixedEF):
|
||||||
"""Section 7.9"""
|
"""Section 7.9"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='6f80', sfid=None, name='EF.NW', desc='Network Name', rec_len={8,8})
|
super().__init__(fid='6f80', sfid=None, name='EF.NW',
|
||||||
|
desc='Network Name', rec_len={8, 8})
|
||||||
self._construct = GsmString(8)
|
self._construct = GsmString(8)
|
||||||
|
|
||||||
|
|
||||||
class EF_Switching(LinFixedEF):
|
class EF_Switching(LinFixedEF):
|
||||||
"""Section 8.4"""
|
"""Section 8.4"""
|
||||||
|
|
||||||
def __init__(self, fid, name, desc):
|
def __init__(self, fid, name, desc):
|
||||||
super().__init__(fid=fid, sfid=None, name=name, desc=desc, rec_len={6,6})
|
super().__init__(fid=fid, sfid=None,
|
||||||
|
name=name, desc=desc, rec_len={6, 6})
|
||||||
self._construct = Struct('next_table_type'/NextTableType,
|
self._construct = Struct('next_table_type'/NextTableType,
|
||||||
'id_of_next_table'/HexAdapter(Bytes(2)),
|
'id_of_next_table'/HexAdapter(Bytes(2)),
|
||||||
'decision_value'/BcdAdapter(Bytes(2)),
|
'decision_value'/BcdAdapter(Bytes(2)),
|
||||||
'string_table_index'/Int8ub)
|
'string_table_index'/Int8ub)
|
||||||
|
|
||||||
|
|
||||||
class EF_Predefined(LinFixedEF):
|
class EF_Predefined(LinFixedEF):
|
||||||
"""Section 8.5"""
|
"""Section 8.5"""
|
||||||
|
|
||||||
def __init__(self, fid, name, desc):
|
def __init__(self, fid, name, desc):
|
||||||
super().__init__(fid=fid, sfid=None, name=name, desc=desc, rec_len={3,3})
|
super().__init__(fid=fid, sfid=None,
|
||||||
|
name=name, desc=desc, rec_len={3, 3})
|
||||||
# header and other records have different structure. WTF !?!
|
# header and other records have different structure. WTF !?!
|
||||||
self._construct = Struct('next_table_type'/NextTableType,
|
self._construct = Struct('next_table_type'/NextTableType,
|
||||||
'id_of_next_table'/HexAdapter(Bytes(2)),
|
'id_of_next_table'/HexAdapter(Bytes(2)),
|
||||||
@@ -212,8 +248,10 @@ class EF_Predefined(LinFixedEF):
|
|||||||
'string_table_index1'/Int8ub)
|
'string_table_index1'/Int8ub)
|
||||||
# TODO: predefined value n, ...
|
# TODO: predefined value n, ...
|
||||||
|
|
||||||
|
|
||||||
class EF_DialledVals(TransparentEF):
|
class EF_DialledVals(TransparentEF):
|
||||||
"""Section 8.6"""
|
"""Section 8.6"""
|
||||||
|
|
||||||
def __init__(self, fid, name, desc):
|
def __init__(self, fid, name, desc):
|
||||||
super().__init__(fid=fid, sfid=None, name=name, desc=desc, size={4, 4})
|
super().__init__(fid=fid, sfid=None, name=name, desc=desc, size={4, 4})
|
||||||
self._construct = Struct('next_table_type'/NextTableType,
|
self._construct = Struct('next_table_type'/NextTableType,
|
||||||
@@ -238,18 +276,31 @@ class DF_EIRENE(CardDF):
|
|||||||
EF_Switching(fid='6f8e', name='EF.CT', desc='Call Type'),
|
EF_Switching(fid='6f8e', name='EF.CT', desc='Call Type'),
|
||||||
EF_Switching(fid='6f8f', name='EF.SC', desc='Short Code'),
|
EF_Switching(fid='6f8f', name='EF.SC', desc='Short Code'),
|
||||||
EF_Predefined(fid='6f88', name='EF.FC', desc='Function Code'),
|
EF_Predefined(fid='6f88', name='EF.FC', desc='Function Code'),
|
||||||
EF_Predefined(fid='6f89', name='EF.Service', desc='VGCS/VBS Service Code'),
|
EF_Predefined(fid='6f89', name='EF.Service',
|
||||||
EF_Predefined(fid='6f8a', name='EF.Call', desc='First digit of the group ID'),
|
desc='VGCS/VBS Service Code'),
|
||||||
EF_Predefined(fid='6f8b', name='EF.FctTeam', desc='Call Type 6 Team Type + Team member function'),
|
EF_Predefined(fid='6f8a', name='EF.Call',
|
||||||
EF_Predefined(fid='6f92', name='EF.Controller', desc='Call Type 7 Controller function code'),
|
desc='First digit of the group ID'),
|
||||||
EF_Predefined(fid='6f8c', name='EF.Gateway', desc='Access to external networks'),
|
EF_Predefined(fid='6f8b', name='EF.FctTeam',
|
||||||
EF_DialledVals(fid='6f81', name='EF.5to8digits', desc='Call Type 2 User Identity Number length'),
|
desc='Call Type 6 Team Type + Team member function'),
|
||||||
EF_DialledVals(fid='6f82', name='EF.2digits', desc='2 digits input'),
|
EF_Predefined(fid='6f92', name='EF.Controller',
|
||||||
EF_DialledVals(fid='6f83', name='EF.8digits', desc='8 digits input'),
|
desc='Call Type 7 Controller function code'),
|
||||||
EF_DialledVals(fid='6f84', name='EF.9digits', desc='9 digits input'),
|
EF_Predefined(fid='6f8c', name='EF.Gateway',
|
||||||
EF_DialledVals(fid='6f85', name='EF.SSSSS', desc='Group call area input'),
|
desc='Access to external networks'),
|
||||||
EF_DialledVals(fid='6f86', name='EF.LLLLL', desc='Location number Call Type 6'),
|
EF_DialledVals(fid='6f81', name='EF.5to8digits',
|
||||||
EF_DialledVals(fid='6f91', name='EF.Location', desc='Location number Call Type 7'),
|
desc='Call Type 2 User Identity Number length'),
|
||||||
EF_DialledVals(fid='6f87', name='EF.FreeNumber', desc='Free Number Call Type 0 and 8'),
|
EF_DialledVals(fid='6f82', name='EF.2digits',
|
||||||
|
desc='2 digits input'),
|
||||||
|
EF_DialledVals(fid='6f83', name='EF.8digits',
|
||||||
|
desc='8 digits input'),
|
||||||
|
EF_DialledVals(fid='6f84', name='EF.9digits',
|
||||||
|
desc='9 digits input'),
|
||||||
|
EF_DialledVals(fid='6f85', name='EF.SSSSS',
|
||||||
|
desc='Group call area input'),
|
||||||
|
EF_DialledVals(fid='6f86', name='EF.LLLLL',
|
||||||
|
desc='Location number Call Type 6'),
|
||||||
|
EF_DialledVals(fid='6f91', name='EF.Location',
|
||||||
|
desc='Location number Call Type 7'),
|
||||||
|
EF_DialledVals(fid='6f87', name='EF.FreeNumber',
|
||||||
|
desc='Free Number Call Type 0 and 8'),
|
||||||
]
|
]
|
||||||
self.add_files(files)
|
self.add_files(files)
|
||||||
|
|||||||
@@ -24,38 +24,56 @@ from pySim.filesystem import *
|
|||||||
from pySim.tlv import *
|
from pySim.tlv import *
|
||||||
|
|
||||||
# Table 91 + Section 8.2.1.2
|
# Table 91 + Section 8.2.1.2
|
||||||
|
|
||||||
|
|
||||||
class ApplicationId(BER_TLV_IE, tag=0x4f):
|
class ApplicationId(BER_TLV_IE, tag=0x4f):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Table 91
|
# Table 91
|
||||||
|
|
||||||
|
|
||||||
class ApplicationLabel(BER_TLV_IE, tag=0x50):
|
class ApplicationLabel(BER_TLV_IE, tag=0x50):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Table 91 + Section 5.3.1.2
|
# Table 91 + Section 5.3.1.2
|
||||||
|
|
||||||
|
|
||||||
class FileReference(BER_TLV_IE, tag=0x51):
|
class FileReference(BER_TLV_IE, tag=0x51):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Table 91
|
# Table 91
|
||||||
|
|
||||||
|
|
||||||
class CommandApdu(BER_TLV_IE, tag=0x52):
|
class CommandApdu(BER_TLV_IE, tag=0x52):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Table 91
|
# Table 91
|
||||||
|
|
||||||
|
|
||||||
class DiscretionaryData(BER_TLV_IE, tag=0x53):
|
class DiscretionaryData(BER_TLV_IE, tag=0x53):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Table 91
|
# Table 91
|
||||||
|
|
||||||
|
|
||||||
class DiscretionaryTemplate(BER_TLV_IE, tag=0x73):
|
class DiscretionaryTemplate(BER_TLV_IE, tag=0x73):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Table 91 + RFC1738 / RFC2396
|
# Table 91 + RFC1738 / RFC2396
|
||||||
|
|
||||||
|
|
||||||
class URL(BER_TLV_IE, tag=0x5f50):
|
class URL(BER_TLV_IE, tag=0x5f50):
|
||||||
_construct = GreedyString('ascii')
|
_construct = GreedyString('ascii')
|
||||||
|
|
||||||
# Table 91
|
# Table 91
|
||||||
|
|
||||||
|
|
||||||
class ApplicationRelatedDOSet(BER_TLV_IE, tag=0x61):
|
class ApplicationRelatedDOSet(BER_TLV_IE, tag=0x61):
|
||||||
_construct = GreedyBytes
|
_construct = GreedyBytes
|
||||||
|
|
||||||
# Section 8.2.1.3 Application Template
|
# Section 8.2.1.3 Application Template
|
||||||
|
|
||||||
|
|
||||||
class ApplicationTemplate(BER_TLV_IE, tag=0x61, nested=[ApplicationId, ApplicationLabel, FileReference,
|
class ApplicationTemplate(BER_TLV_IE, tag=0x61, nested=[ApplicationId, ApplicationLabel, FileReference,
|
||||||
CommandApdu, DiscretionaryData, DiscretionaryTemplate, URL,
|
CommandApdu, DiscretionaryData, DiscretionaryTemplate, URL,
|
||||||
ApplicationRelatedDOSet]):
|
ApplicationRelatedDOSet]):
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ of a file or record in its JSON representation.
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
def js_path_find(js_dict, js_path):
|
def js_path_find(js_dict, js_path):
|
||||||
"""Find/Match a JSON path within a given JSON-serializable dict.
|
"""Find/Match a JSON path within a given JSON-serializable dict.
|
||||||
Args:
|
Args:
|
||||||
@@ -35,6 +36,7 @@ def js_path_find(js_dict, js_path):
|
|||||||
jsonpath_expr = jsonpath_ng.parse(js_path)
|
jsonpath_expr = jsonpath_ng.parse(js_path)
|
||||||
return jsonpath_expr.find(js_dict)
|
return jsonpath_expr.find(js_dict)
|
||||||
|
|
||||||
|
|
||||||
def js_path_modify(js_dict, js_path, new_val):
|
def js_path_modify(js_dict, js_path, new_val):
|
||||||
"""Find/Match a JSON path within a given JSON-serializable dict.
|
"""Find/Match a JSON path within a given JSON-serializable dict.
|
||||||
Args:
|
Args:
|
||||||
@@ -45,4 +47,3 @@ def js_path_modify(js_dict, js_path, new_val):
|
|||||||
jsonpath_expr = jsonpath_ng.parse(js_path)
|
jsonpath_expr = jsonpath_ng.parse(js_path)
|
||||||
jsonpath_expr.find(js_dict)
|
jsonpath_expr.find(js_dict)
|
||||||
jsonpath_expr.update(js_dict, new_val)
|
jsonpath_expr.update(js_dict, new_val)
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from pySim.utils import all_subclasses
|
|||||||
import abc
|
import abc
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
|
|
||||||
def _mf_select_test(scc: SimCardCommands, cla_byte: str, sel_ctrl: str) -> bool:
|
def _mf_select_test(scc: SimCardCommands, cla_byte: str, sel_ctrl: str) -> bool:
|
||||||
cla_byte_bak = scc.cla_byte
|
cla_byte_bak = scc.cla_byte
|
||||||
sel_ctrl_bak = scc.sel_ctrl
|
sel_ctrl_bak = scc.sel_ctrl
|
||||||
@@ -45,12 +46,14 @@ def _mf_select_test(scc:SimCardCommands, cla_byte:str, sel_ctrl:str) -> bool:
|
|||||||
scc.sel_ctrl = sel_ctrl_bak
|
scc.sel_ctrl = sel_ctrl_bak
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
|
|
||||||
def match_uicc(scc: SimCardCommands) -> bool:
|
def match_uicc(scc: SimCardCommands) -> bool:
|
||||||
""" Try to access MF via UICC APDUs (3GPP TS 102.221), if this works, the
|
""" Try to access MF via UICC APDUs (3GPP TS 102.221), if this works, the
|
||||||
card is considered a UICC card.
|
card is considered a UICC card.
|
||||||
"""
|
"""
|
||||||
return _mf_select_test(scc, "00", "0004")
|
return _mf_select_test(scc, "00", "0004")
|
||||||
|
|
||||||
|
|
||||||
def match_sim(scc: SimCardCommands) -> bool:
|
def match_sim(scc: SimCardCommands) -> bool:
|
||||||
""" Try to access MF via 2G APDUs (3GPP TS 11.11), if this works, the card
|
""" Try to access MF via 2G APDUs (3GPP TS 11.11), if this works, the card
|
||||||
is also a simcard. This will be the case for most UICC cards, but there may
|
is also a simcard. This will be the case for most UICC cards, but there may
|
||||||
@@ -58,6 +61,7 @@ def match_sim(scc:SimCardCommands) -> bool:
|
|||||||
"""
|
"""
|
||||||
return _mf_select_test(scc, "a0", "0000")
|
return _mf_select_test(scc, "a0", "0000")
|
||||||
|
|
||||||
|
|
||||||
class CardProfile(object):
|
class CardProfile(object):
|
||||||
"""A Card Profile describes a card, it's filesystem hierarchy, an [initial] list of
|
"""A Card Profile describes a card, it's filesystem hierarchy, an [initial] list of
|
||||||
applications as well as profile-specific SW and shell commands. Every card has
|
applications as well as profile-specific SW and shell commands. Every card has
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ mac_length = {
|
|||||||
1: 4
|
1: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EF_PIN(TransparentEF):
|
class EF_PIN(TransparentEF):
|
||||||
def __init__(self, fid, name):
|
def __init__(self, fid, name):
|
||||||
super().__init__(fid, name=name, desc='%s PIN file' % name)
|
super().__init__(fid, name=name, desc='%s PIN file' % name)
|
||||||
|
|
||||||
def _decode_bin(self, raw_bin_data):
|
def _decode_bin(self, raw_bin_data):
|
||||||
u = unpack('!BBB8s', raw_bin_data[:11])
|
u = unpack('!BBB8s', raw_bin_data[:11])
|
||||||
res = {'enabled': (True, False)[u[0] & 0x01],
|
res = {'enabled': (True, False)[u[0] & 0x01],
|
||||||
@@ -65,9 +67,11 @@ class EF_PIN(TransparentEF):
|
|||||||
res['puk'] = u2[2].hex()
|
res['puk'] = u2[2].hex()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class EF_MILENAGE_CFG(TransparentEF):
|
class EF_MILENAGE_CFG(TransparentEF):
|
||||||
def __init__(self, fid='6f21', name='EF.MILENAGE_CFG', desc='Milenage connfiguration'):
|
def __init__(self, fid='6f21', name='EF.MILENAGE_CFG', desc='Milenage connfiguration'):
|
||||||
super().__init__(fid, name=name, desc=desc)
|
super().__init__(fid, name=name, desc=desc)
|
||||||
|
|
||||||
def _decode_bin(self, raw_bin_data):
|
def _decode_bin(self, raw_bin_data):
|
||||||
u = unpack('!BBBBB16s16s16s16s16s', raw_bin_data)
|
u = unpack('!BBBBB16s16s16s16s16s', raw_bin_data)
|
||||||
return {'r1': u[0], 'r2': u[1], 'r3': u[2], 'r4': u[3], 'r5': u[4],
|
return {'r1': u[0], 'r2': u[1], 'r3': u[2], 'r4': u[3], 'r5': u[4],
|
||||||
@@ -78,9 +82,11 @@ class EF_MILENAGE_CFG(TransparentEF):
|
|||||||
'c5': u[9].hex(),
|
'c5': u[9].hex(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EF_0348_KEY(LinFixedEF):
|
class EF_0348_KEY(LinFixedEF):
|
||||||
def __init__(self, fid='6f22', name='EF.0348_KEY', desc='TS 03.48 OTA Keys'):
|
def __init__(self, fid='6f22', name='EF.0348_KEY', desc='TS 03.48 OTA Keys'):
|
||||||
super().__init__(fid, name=name, desc=desc, rec_len={27, 35})
|
super().__init__(fid, name=name, desc=desc, rec_len={27, 35})
|
||||||
|
|
||||||
def _decode_record_bin(self, raw_bin_data):
|
def _decode_record_bin(self, raw_bin_data):
|
||||||
u = unpack('!BBB', raw_bin_data[0:3])
|
u = unpack('!BBB', raw_bin_data[0:3])
|
||||||
key_algo = (u[2] >> 6) & 1
|
key_algo = (u[2] >> 6) & 1
|
||||||
@@ -94,32 +100,40 @@ class EF_0348_KEY(LinFixedEF):
|
|||||||
'key': raw_bin_data[3:key_length].hex()
|
'key': raw_bin_data[3:key_length].hex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EF_0348_COUNT(LinFixedEF):
|
class EF_0348_COUNT(LinFixedEF):
|
||||||
def __init__(self, fid='6f23', name='EF.0348_COUNT', desc='TS 03.48 OTA Counters'):
|
def __init__(self, fid='6f23', name='EF.0348_COUNT', desc='TS 03.48 OTA Counters'):
|
||||||
super().__init__(fid, name=name, desc=desc, rec_len={7, 7})
|
super().__init__(fid, name=name, desc=desc, rec_len={7, 7})
|
||||||
|
|
||||||
def _decode_record_bin(self, raw_bin_data):
|
def _decode_record_bin(self, raw_bin_data):
|
||||||
u = unpack('!BB5s', raw_bin_data)
|
u = unpack('!BB5s', raw_bin_data)
|
||||||
return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2]}
|
return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2]}
|
||||||
|
|
||||||
|
|
||||||
class EF_SIM_AUTH_COUNTER(TransparentEF):
|
class EF_SIM_AUTH_COUNTER(TransparentEF):
|
||||||
def __init__(self, fid='af24', name='EF.SIM_AUTH_COUNTER'):
|
def __init__(self, fid='af24', name='EF.SIM_AUTH_COUNTER'):
|
||||||
super().__init__(fid, name=name, desc='Number of remaining RUN GSM ALGORITHM executions')
|
super().__init__(fid, name=name, desc='Number of remaining RUN GSM ALGORITHM executions')
|
||||||
self._construct = Struct('num_run_gsm_algo_remain'/Int32ub)
|
self._construct = Struct('num_run_gsm_algo_remain'/Int32ub)
|
||||||
|
|
||||||
|
|
||||||
class EF_GP_COUNT(LinFixedEF):
|
class EF_GP_COUNT(LinFixedEF):
|
||||||
def __init__(self, fid='6f26', name='EF.GP_COUNT', desc='GP SCP02 Counters'):
|
def __init__(self, fid='6f26', name='EF.GP_COUNT', desc='GP SCP02 Counters'):
|
||||||
super().__init__(fid, name=name, desc=desc, rec_len={5, 5})
|
super().__init__(fid, name=name, desc=desc, rec_len={5, 5})
|
||||||
|
|
||||||
def _decode_record_bin(self, raw_bin_data):
|
def _decode_record_bin(self, raw_bin_data):
|
||||||
u = unpack('!BBHB', raw_bin_data)
|
u = unpack('!BBHB', raw_bin_data)
|
||||||
return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2], 'rfu': u[3]}
|
return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2], 'rfu': u[3]}
|
||||||
|
|
||||||
|
|
||||||
class EF_GP_DIV_DATA(LinFixedEF):
|
class EF_GP_DIV_DATA(LinFixedEF):
|
||||||
def __init__(self, fid='6f27', name='EF.GP_DIV_DATA', desc='GP SCP02 key diversification data'):
|
def __init__(self, fid='6f27', name='EF.GP_DIV_DATA', desc='GP SCP02 key diversification data'):
|
||||||
super().__init__(fid, name=name, desc=desc, rec_len={12, 12})
|
super().__init__(fid, name=name, desc=desc, rec_len={12, 12})
|
||||||
|
|
||||||
def _decode_record_bin(self, raw_bin_data):
|
def _decode_record_bin(self, raw_bin_data):
|
||||||
u = unpack('!BB8s', raw_bin_data)
|
u = unpack('!BB8s', raw_bin_data)
|
||||||
return {'sec_domain': u[0], 'key_set_version': u[1], 'key_div_data': u[2].hex()}
|
return {'sec_domain': u[0], 'key_set_version': u[1], 'key_div_data': u[2].hex()}
|
||||||
|
|
||||||
|
|
||||||
class EF_SIM_AUTH_KEY(TransparentEF):
|
class EF_SIM_AUTH_KEY(TransparentEF):
|
||||||
def __init__(self, fid='6f20', name='EF.SIM_AUTH_KEY'):
|
def __init__(self, fid='6f20', name='EF.SIM_AUTH_KEY'):
|
||||||
super().__init__(fid, name=name, desc='USIM authentication key')
|
super().__init__(fid, name=name, desc='USIM authentication key')
|
||||||
@@ -129,10 +143,15 @@ class EF_SIM_AUTH_KEY(TransparentEF):
|
|||||||
'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3))
|
'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3))
|
||||||
self._construct = Struct('cfg'/CfgByte,
|
self._construct = Struct('cfg'/CfgByte,
|
||||||
'key'/Bytes(16),
|
'key'/Bytes(16),
|
||||||
'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)),
|
'op' /
|
||||||
'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16))
|
If(this.cfg.algorithm == 'milenage' and not this.cfg.use_opc_instead_of_op, Bytes(
|
||||||
|
16)),
|
||||||
|
'opc' /
|
||||||
|
If(this.cfg.algorithm == 'milenage' and this.cfg.use_opc_instead_of_op, Bytes(
|
||||||
|
16))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DF_SYSTEM(CardDF):
|
class DF_SYSTEM(CardDF):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(fid='a515', name='DF.SYSTEM', desc='CardOS specifics')
|
super().__init__(fid='a515', name='DF.SYSTEM', desc='CardOS specifics')
|
||||||
@@ -156,6 +175,7 @@ class DF_SYSTEM(CardDF):
|
|||||||
def decode_select_response(self, resp_hex):
|
def decode_select_response(self, resp_hex):
|
||||||
return pySim.ts_102_221.CardProfileUICC.decode_select_response(resp_hex)
|
return pySim.ts_102_221.CardProfileUICC.decode_select_response(resp_hex)
|
||||||
|
|
||||||
|
|
||||||
class EF_USIM_SQN(TransparentEF):
|
class EF_USIM_SQN(TransparentEF):
|
||||||
def __init__(self, fid='af30', name='EF.USIM_SQN'):
|
def __init__(self, fid='af30', name='EF.USIM_SQN'):
|
||||||
super().__init__(fid, name=name, desc='SQN parameters for AKA')
|
super().__init__(fid, name=name, desc='SQN parameters for AKA')
|
||||||
@@ -165,9 +185,11 @@ class EF_USIM_SQN(TransparentEF):
|
|||||||
Flag2 = BitStruct('rfu'/BitsRFU(5), 'dont_clear_amf_for_macs'/Bit,
|
Flag2 = BitStruct('rfu'/BitsRFU(5), 'dont_clear_amf_for_macs'/Bit,
|
||||||
'aus_concealed'/Bit, 'autn_concealed'/Bit)
|
'aus_concealed'/Bit, 'autn_concealed'/Bit)
|
||||||
self._construct = Struct('flag1'/Flag1, 'flag2'/Flag2,
|
self._construct = Struct('flag1'/Flag1, 'flag2'/Flag2,
|
||||||
'delta_max'/BytesInteger(6), 'age_limit'/BytesInteger(6),
|
'delta_max' /
|
||||||
|
BytesInteger(6), 'age_limit'/BytesInteger(6),
|
||||||
'freshness'/GreedyRange(BytesInteger(6)))
|
'freshness'/GreedyRange(BytesInteger(6)))
|
||||||
|
|
||||||
|
|
||||||
class EF_USIM_AUTH_KEY(TransparentEF):
|
class EF_USIM_AUTH_KEY(TransparentEF):
|
||||||
def __init__(self, fid='af20', name='EF.USIM_AUTH_KEY'):
|
def __init__(self, fid='af20', name='EF.USIM_AUTH_KEY'):
|
||||||
super().__init__(fid, name=name, desc='USIM authentication key')
|
super().__init__(fid, name=name, desc='USIM authentication key')
|
||||||
@@ -177,9 +199,15 @@ class EF_USIM_AUTH_KEY(TransparentEF):
|
|||||||
'algorithm'/Enum(Nibble, milenage=4, sha1_aka=5, xor=15))
|
'algorithm'/Enum(Nibble, milenage=4, sha1_aka=5, xor=15))
|
||||||
self._construct = Struct('cfg'/CfgByte,
|
self._construct = Struct('cfg'/CfgByte,
|
||||||
'key'/Bytes(16),
|
'key'/Bytes(16),
|
||||||
'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)),
|
'op' /
|
||||||
'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16))
|
If(this.cfg.algorithm == 'milenage' and not this.cfg.use_opc_instead_of_op, Bytes(
|
||||||
|
16)),
|
||||||
|
'opc' /
|
||||||
|
If(this.cfg.algorithm == 'milenage' and this.cfg.use_opc_instead_of_op, Bytes(
|
||||||
|
16))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EF_USIM_AUTH_KEY_2G(TransparentEF):
|
class EF_USIM_AUTH_KEY_2G(TransparentEF):
|
||||||
def __init__(self, fid='af22', name='EF.USIM_AUTH_KEY_2G'):
|
def __init__(self, fid='af22', name='EF.USIM_AUTH_KEY_2G'):
|
||||||
super().__init__(fid, name=name, desc='USIM authentication key in 2G context')
|
super().__init__(fid, name=name, desc='USIM authentication key in 2G context')
|
||||||
@@ -189,31 +217,40 @@ class EF_USIM_AUTH_KEY_2G(TransparentEF):
|
|||||||
'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3))
|
'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3))
|
||||||
self._construct = Struct('cfg'/CfgByte,
|
self._construct = Struct('cfg'/CfgByte,
|
||||||
'key'/Bytes(16),
|
'key'/Bytes(16),
|
||||||
'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)),
|
'op' /
|
||||||
'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16))
|
If(this.cfg.algorithm == 'milenage' and not this.cfg.use_opc_instead_of_op, Bytes(
|
||||||
|
16)),
|
||||||
|
'opc' /
|
||||||
|
If(this.cfg.algorithm == 'milenage' and this.cfg.use_opc_instead_of_op, Bytes(
|
||||||
|
16))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EF_GBA_SK(TransparentEF):
|
class EF_GBA_SK(TransparentEF):
|
||||||
def __init__(self, fid='af31', name='EF.GBA_SK'):
|
def __init__(self, fid='af31', name='EF.GBA_SK'):
|
||||||
super().__init__(fid, name=name, desc='Secret key for GBA key derivation')
|
super().__init__(fid, name=name, desc='Secret key for GBA key derivation')
|
||||||
self._construct = GreedyBytes
|
self._construct = GreedyBytes
|
||||||
|
|
||||||
|
|
||||||
class EF_GBA_REC_LIST(TransparentEF):
|
class EF_GBA_REC_LIST(TransparentEF):
|
||||||
def __init__(self, fid='af32', name='EF.GBA_REC_LIST'):
|
def __init__(self, fid='af32', name='EF.GBA_REC_LIST'):
|
||||||
super().__init__(fid, name=name, desc='Secret key for GBA key derivation')
|
super().__init__(fid, name=name, desc='Secret key for GBA key derivation')
|
||||||
# integers representing record numbers in EF-GBANL
|
# integers representing record numbers in EF-GBANL
|
||||||
self._construct = GreedyRange(Int8ub)
|
self._construct = GreedyRange(Int8ub)
|
||||||
|
|
||||||
|
|
||||||
class EF_GBA_INT_KEY(LinFixedEF):
|
class EF_GBA_INT_KEY(LinFixedEF):
|
||||||
def __init__(self, fid='af33', name='EF.GBA_INT_KEY'):
|
def __init__(self, fid='af33', name='EF.GBA_INT_KEY'):
|
||||||
super().__init__(fid, name=name, desc='Secret key for GBA key derivation', rec_len={32,32})
|
super().__init__(fid, name=name,
|
||||||
|
desc='Secret key for GBA key derivation', rec_len={32, 32})
|
||||||
self._construct = GreedyBytes
|
self._construct = GreedyBytes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SysmocomSJA2(CardModel):
|
class SysmocomSJA2(CardModel):
|
||||||
_atrs = ["3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9",
|
_atrs = ["3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9",
|
||||||
"3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2",
|
"3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2",
|
||||||
"3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"]
|
"3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_files(cls, rs: RuntimeState):
|
def add_files(cls, rs: RuntimeState):
|
||||||
"""Add sysmocom SJA2 specific files to given RuntimeState."""
|
"""Add sysmocom SJA2 specific files to given RuntimeState."""
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from pySim.exceptions import *
|
|||||||
import inspect
|
import inspect
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
|
|
||||||
class TlvMeta(abc.ABCMeta):
|
class TlvMeta(abc.ABCMeta):
|
||||||
"""Metaclass which we use to set some class variables at the time of defining a subclass.
|
"""Metaclass which we use to set some class variables at the time of defining a subclass.
|
||||||
This allows us to create subclasses for each TLV/IE type, where the class represents fixed
|
This allows us to create subclasses for each TLV/IE type, where the class represents fixed
|
||||||
@@ -54,6 +55,7 @@ class TlvMeta(abc.ABCMeta):
|
|||||||
x.nested_collection_cls = cls
|
x.nested_collection_cls = cls
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
class TlvCollectionMeta(abc.ABCMeta):
|
class TlvCollectionMeta(abc.ABCMeta):
|
||||||
"""Metaclass which we use to set some class variables at the time of defining a subclass.
|
"""Metaclass which we use to set some class variables at the time of defining a subclass.
|
||||||
This allows us to create subclasses for each Collection type, where the class represents fixed
|
This allows us to create subclasses for each Collection type, where the class represents fixed
|
||||||
@@ -72,6 +74,7 @@ class Transcodable(abc.ABC):
|
|||||||
* via a 'construct' object stored in a derived class' _construct variable, or
|
* via a 'construct' object stored in a derived class' _construct variable, or
|
||||||
* via a 'construct' object stored in an instance _construct variable, or
|
* via a 'construct' object stored in an instance _construct variable, or
|
||||||
* via a derived class' _{to,from}_bytes() methods."""
|
* via a derived class' _{to,from}_bytes() methods."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.encoded = None
|
self.encoded = None
|
||||||
self.decoded = None
|
self.decoded = None
|
||||||
@@ -113,6 +116,7 @@ class Transcodable(abc.ABC):
|
|||||||
def _from_bytes(self, do: bytes):
|
def _from_bytes(self, do: bytes):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class IE(Transcodable, metaclass=TlvMeta):
|
class IE(Transcodable, metaclass=TlvMeta):
|
||||||
# we specify the metaclass so any downstream subclasses will automatically use it
|
# we specify the metaclass so any downstream subclasses will automatically use it
|
||||||
"""Base class for various Information Elements. We understand the notion of a hierarchy
|
"""Base class for various Information Elements. We understand the notion of a hierarchy
|
||||||
@@ -188,6 +192,7 @@ class IE(Transcodable, metaclass=TlvMeta):
|
|||||||
|
|
||||||
class TLV_IE(IE):
|
class TLV_IE(IE):
|
||||||
"""Abstract base class for various TLV type Information Elements."""
|
"""Abstract base class for various TLV type Information Elements."""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@@ -240,6 +245,7 @@ class TLV_IE(IE):
|
|||||||
|
|
||||||
class BER_TLV_IE(TLV_IE):
|
class BER_TLV_IE(TLV_IE):
|
||||||
"""TLV_IE formatted as ASN.1 BER described in ITU-T X.690 8.1.2."""
|
"""TLV_IE formatted as ASN.1 BER described in ITU-T X.690 8.1.2."""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@@ -264,6 +270,7 @@ class BER_TLV_IE(TLV_IE):
|
|||||||
|
|
||||||
class COMPR_TLV_IE(TLV_IE):
|
class COMPR_TLV_IE(TLV_IE):
|
||||||
"""TLV_IE formated as COMPREHENSION-TLV as described in ETSI TS 101 220."""
|
"""TLV_IE formated as COMPREHENSION-TLV as described in ETSI TS 101 220."""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.comprehension = False
|
self.comprehension = False
|
||||||
@@ -294,6 +301,7 @@ class TLV_IE_Collection(metaclass=TlvCollectionMeta):
|
|||||||
of each DO."""
|
of each DO."""
|
||||||
# this is overridden by the TlvCollectionMeta metaclass, if it is used to create subclasses
|
# this is overridden by the TlvCollectionMeta metaclass, if it is used to create subclasses
|
||||||
possible_nested = []
|
possible_nested = []
|
||||||
|
|
||||||
def __init__(self, desc=None, **kwargs):
|
def __init__(self, desc=None, **kwargs):
|
||||||
self.desc = desc
|
self.desc = desc
|
||||||
#print("possible_nested: ", self.possible_nested)
|
#print("possible_nested: ", self.possible_nested)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from pySim.utils import sw_match, b2h, h2b, i2h
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class ApduTracer:
|
class ApduTracer:
|
||||||
def trace_command(self, cmd):
|
def trace_command(self, cmd):
|
||||||
pass
|
pass
|
||||||
@@ -105,7 +106,7 @@ class LinkBase(abc.ABC):
|
|||||||
"""
|
"""
|
||||||
data, sw = self.send_apdu_raw(pdu)
|
data, sw = self.send_apdu_raw(pdu)
|
||||||
|
|
||||||
# When whe have sent the first APDU, the SW may indicate that there are response bytes
|
# When we have sent the first APDU, the SW may indicate that there are response bytes
|
||||||
# available. There are two SWs commonly used for this 9fxx (sim) and 61xx (usim), where
|
# available. There are two SWs commonly used for this 9fxx (sim) and 61xx (usim), where
|
||||||
# xx is the number of response bytes available.
|
# xx is the number of response bytes available.
|
||||||
# See also:
|
# See also:
|
||||||
@@ -185,11 +186,13 @@ class LinkBase(abc.ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
Tuple of (decoded_data, sw)
|
Tuple of (decoded_data, sw)
|
||||||
"""
|
"""
|
||||||
(rsp, sw) = self.send_apdu_constr(cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr)
|
(rsp, sw) = self.send_apdu_constr(cla, ins,
|
||||||
|
p1, p2, cmd_constr, cmd_data, resp_constr)
|
||||||
if not sw_match(sw, sw_exp):
|
if not sw_match(sw, sw_exp):
|
||||||
raise SwMatchError(sw, sw_exp.lower(), self.sw_interpreter)
|
raise SwMatchError(sw, sw_exp.lower(), self.sw_interpreter)
|
||||||
return (rsp, sw)
|
return (rsp, sw)
|
||||||
|
|
||||||
|
|
||||||
def argparse_add_reader_args(arg_parser):
|
def argparse_add_reader_args(arg_parser):
|
||||||
"""Add all reader related arguments to the given argparse.Argumentparser instance."""
|
"""Add all reader related arguments to the given argparse.Argumentparser instance."""
|
||||||
serial_group = arg_parser.add_argument_group('Serial Reader')
|
serial_group = arg_parser.add_argument_group('Serial Reader')
|
||||||
@@ -214,6 +217,7 @@ def argparse_add_reader_args(arg_parser):
|
|||||||
|
|
||||||
return arg_parser
|
return arg_parser
|
||||||
|
|
||||||
|
|
||||||
def init_reader(opts, **kwargs) -> Optional[LinkBase]:
|
def init_reader(opts, **kwargs) -> Optional[LinkBase]:
|
||||||
"""
|
"""
|
||||||
Init card reader driver
|
Init card reader driver
|
||||||
@@ -231,15 +235,18 @@ def init_reader(opts, **kwargs) -> Optional[LinkBase]:
|
|||||||
elif opts.modem_dev is not None:
|
elif opts.modem_dev is not None:
|
||||||
print("Using modem for Generic SIM Access (3GPP TS 27.007)")
|
print("Using modem for Generic SIM Access (3GPP TS 27.007)")
|
||||||
from pySim.transport.modem_atcmd import ModemATCommandLink
|
from pySim.transport.modem_atcmd import ModemATCommandLink
|
||||||
sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud, **kwargs)
|
sl = ModemATCommandLink(
|
||||||
|
device=opts.modem_dev, baudrate=opts.modem_baud, **kwargs)
|
||||||
else: # Serial reader is default
|
else: # Serial reader is default
|
||||||
print("Using serial reader interface")
|
print("Using serial reader interface")
|
||||||
from pySim.transport.serial import SerialSimLink
|
from pySim.transport.serial import SerialSimLink
|
||||||
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate, **kwargs)
|
sl = SerialSimLink(device=opts.device,
|
||||||
|
baudrate=opts.baudrate, **kwargs)
|
||||||
return sl
|
return sl
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if str(e):
|
if str(e):
|
||||||
print("Card reader initialization failed with exception:\n" + str(e))
|
print("Card reader initialization failed with exception:\n" + str(e))
|
||||||
else:
|
else:
|
||||||
print("Card reader initialization failed with an exception of type:\n" + str(type(e)))
|
print(
|
||||||
|
"Card reader initialization failed with an exception of type:\n" + str(type(e)))
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from pySim.transport import LinkBase
|
|||||||
from pySim.exceptions import *
|
from pySim.exceptions import *
|
||||||
from pySim.utils import h2b, b2h
|
from pySim.utils import h2b, b2h
|
||||||
|
|
||||||
|
|
||||||
class L1CTLMessage(object):
|
class L1CTLMessage(object):
|
||||||
|
|
||||||
# Every (encoded) L1CTL message has the following structure:
|
# Every (encoded) L1CTL message has the following structure:
|
||||||
@@ -42,6 +43,7 @@ class L1CTLMessage(object):
|
|||||||
def gen_msg(self):
|
def gen_msg(self):
|
||||||
return struct.pack("!H", len(self.data)) + self.data
|
return struct.pack("!H", len(self.data)) + self.data
|
||||||
|
|
||||||
|
|
||||||
class L1CTLMessageReset(L1CTLMessage):
|
class L1CTLMessageReset(L1CTLMessage):
|
||||||
|
|
||||||
# L1CTL message types
|
# L1CTL message types
|
||||||
@@ -58,6 +60,7 @@ class L1CTLMessageReset(L1CTLMessage):
|
|||||||
super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
|
super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
|
||||||
self.data += struct.pack("Bxxx", type)
|
self.data += struct.pack("Bxxx", type)
|
||||||
|
|
||||||
|
|
||||||
class L1CTLMessageSIM(L1CTLMessage):
|
class L1CTLMessageSIM(L1CTLMessage):
|
||||||
|
|
||||||
# SIM related message types
|
# SIM related message types
|
||||||
@@ -68,6 +71,7 @@ class L1CTLMessageSIM(L1CTLMessage):
|
|||||||
super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
|
super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
|
||||||
self.data += pdu
|
self.data += pdu
|
||||||
|
|
||||||
|
|
||||||
class CalypsoSimLink(LinkBase):
|
class CalypsoSimLink(LinkBase):
|
||||||
"""Transport Link for Calypso based phones."""
|
"""Transport Link for Calypso based phones."""
|
||||||
|
|
||||||
@@ -75,7 +79,8 @@ class CalypsoSimLink(LinkBase):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
# Make sure that a given socket path exists
|
# Make sure that a given socket path exists
|
||||||
if not os.path.exists(sock_path):
|
if not os.path.exists(sock_path):
|
||||||
raise ReaderError("There is no such ('%s') UNIX socket" % sock_path)
|
raise ReaderError(
|
||||||
|
"There is no such ('%s') UNIX socket" % sock_path)
|
||||||
|
|
||||||
print("Connecting to osmocon at '%s'..." % sock_path)
|
print("Connecting to osmocon at '%s'..." % sock_path)
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ from pySim.exceptions import *
|
|||||||
# HACK: if somebody needs to debug this thing
|
# HACK: if somebody needs to debug this thing
|
||||||
# log.root.setLevel(log.DEBUG)
|
# log.root.setLevel(log.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
class ModemATCommandLink(LinkBase):
|
class ModemATCommandLink(LinkBase):
|
||||||
"""Transport Link for 3GPP TS 27.007 compliant modems."""
|
"""Transport Link for 3GPP TS 27.007 compliant modems."""
|
||||||
|
|
||||||
def __init__(self, device: str = '/dev/ttyUSB0', baudrate: int = 115200, **kwargs):
|
def __init__(self, device: str = '/dev/ttyUSB0', baudrate: int = 115200, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self._sl = serial.Serial(device, baudrate, timeout=5)
|
self._sl = serial.Serial(device, baudrate, timeout=5)
|
||||||
@@ -82,7 +84,8 @@ class ModemATCommandLink(LinkBase):
|
|||||||
break
|
break
|
||||||
time.sleep(patience)
|
time.sleep(patience)
|
||||||
its += 1
|
its += 1
|
||||||
log.debug('Command took %0.6fs (%d cycles a %fs)', time.time() - t_start, its, patience)
|
log.debug('Command took %0.6fs (%d cycles a %fs)',
|
||||||
|
time.time() - t_start, its, patience)
|
||||||
|
|
||||||
if self._echo:
|
if self._echo:
|
||||||
# Skip echo chars
|
# Skip echo chars
|
||||||
@@ -113,7 +116,8 @@ class ModemATCommandLink(LinkBase):
|
|||||||
elif result[-1] == b'AT\r\r\nOK':
|
elif result[-1] == b'AT\r\r\nOK':
|
||||||
self._echo = True
|
self._echo = True
|
||||||
return
|
return
|
||||||
raise ReaderError('Interface \'%s\' does not respond to \'AT\' command' % self._device)
|
raise ReaderError(
|
||||||
|
'Interface \'%s\' does not respond to \'AT\' command' % self._device)
|
||||||
|
|
||||||
def reset_card(self):
|
def reset_card(self):
|
||||||
# Reset the modem, just to be sure
|
# Reset the modem, just to be sure
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ class PcscSimLink(LinkBase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def wait_for_card(self, timeout: int = None, newcardonly: bool = False):
|
def wait_for_card(self, timeout: int = None, newcardonly: bool = False):
|
||||||
cr = CardRequest(readers=[self._reader], timeout=timeout, newcardonly=newcardonly)
|
cr = CardRequest(readers=[self._reader],
|
||||||
|
timeout=timeout, newcardonly=newcardonly)
|
||||||
try:
|
try:
|
||||||
cr.waitforcard()
|
cr.waitforcard()
|
||||||
except CardRequestTimeoutException:
|
except CardRequestTimeoutException:
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ class SerialSimLink(LinkBase):
|
|||||||
self._sl.write(b)
|
self._sl.write(b)
|
||||||
r = self._sl.read()
|
r = self._sl.read()
|
||||||
if r != b: # TX and RX are tied, so we must clear the echo
|
if r != b: # TX and RX are tied, so we must clear the echo
|
||||||
raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
|
raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (
|
||||||
|
ord(b), '%02x' % ord(r) if r else '(nil)'))
|
||||||
|
|
||||||
def _tx_string(self, s):
|
def _tx_string(self, s):
|
||||||
"""This is only safe if it's guaranteed the card won't send any data
|
"""This is only safe if it's guaranteed the card won't send any data
|
||||||
@@ -176,7 +177,8 @@ class SerialSimLink(LinkBase):
|
|||||||
self._sl.write(s)
|
self._sl.write(s)
|
||||||
r = self._sl.read(len(s))
|
r = self._sl.read(len(s))
|
||||||
if r != s: # TX and RX are tied, so we must clear the echo
|
if r != s: # TX and RX are tied, so we must clear the echo
|
||||||
raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
|
raise ProtocolError(
|
||||||
|
"Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
|
||||||
|
|
||||||
def _rx_byte(self):
|
def _rx_byte(self):
|
||||||
return self._sl.read()
|
return self._sl.read()
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ FCP_Proprietary_TLV_MAP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ETSI TS 102 221 11.1.1.4.3
|
# ETSI TS 102 221 11.1.1.4.3
|
||||||
|
|
||||||
|
|
||||||
def interpret_file_descriptor(in_hex):
|
def interpret_file_descriptor(in_hex):
|
||||||
in_bin = h2b(in_hex)
|
in_bin = h2b(in_hex)
|
||||||
out = {}
|
out = {}
|
||||||
@@ -140,6 +142,8 @@ def interpret_file_descriptor(in_hex):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
# ETSI TS 102 221 11.1.1.4.9
|
# ETSI TS 102 221 11.1.1.4.9
|
||||||
|
|
||||||
|
|
||||||
def interpret_life_cycle_sts_int(in_hex):
|
def interpret_life_cycle_sts_int(in_hex):
|
||||||
lcsi = int(in_hex, 16)
|
lcsi = int(in_hex, 16)
|
||||||
if lcsi == 0x00:
|
if lcsi == 0x00:
|
||||||
@@ -157,6 +161,7 @@ def interpret_life_cycle_sts_int(in_hex):
|
|||||||
else:
|
else:
|
||||||
return in_hex
|
return in_hex
|
||||||
|
|
||||||
|
|
||||||
# ETSI TS 102 221 11.1.1.4.10
|
# ETSI TS 102 221 11.1.1.4.10
|
||||||
FCP_Pin_Status_TLV_MAP = {
|
FCP_Pin_Status_TLV_MAP = {
|
||||||
'90': 'ps_do',
|
'90': 'ps_do',
|
||||||
@@ -164,12 +169,14 @@ FCP_Pin_Status_TLV_MAP = {
|
|||||||
'83': 'key_reference',
|
'83': 'key_reference',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def interpret_ps_templ_do(in_hex):
|
def interpret_ps_templ_do(in_hex):
|
||||||
# cannot use the 'TLV' parser due to repeating tags
|
# cannot use the 'TLV' parser due to repeating tags
|
||||||
#psdo_tlv = TLV(FCP_Pin_Status_TLV_MAP)
|
#psdo_tlv = TLV(FCP_Pin_Status_TLV_MAP)
|
||||||
# return psdo_tlv.parse(in_hex)
|
# return psdo_tlv.parse(in_hex)
|
||||||
return in_hex
|
return in_hex
|
||||||
|
|
||||||
|
|
||||||
# 'interpreter' functions for each tag
|
# 'interpreter' functions for each tag
|
||||||
FCP_interpreter_map = {
|
FCP_interpreter_map = {
|
||||||
'80': lambda x: int(x, 16),
|
'80': lambda x: int(x, 16),
|
||||||
@@ -186,6 +193,8 @@ FCP_prorietary_interpreter_map = {
|
|||||||
# accept unknown tags. It also doesn't raise a specific exception type but
|
# accept unknown tags. It also doesn't raise a specific exception type but
|
||||||
# just the generic ValueError, so we cannot ignore those either. Instead,
|
# just the generic ValueError, so we cannot ignore those either. Instead,
|
||||||
# we insert a dict entry for every possible proprietary tag permitted
|
# we insert a dict entry for every possible proprietary tag permitted
|
||||||
|
|
||||||
|
|
||||||
def fixup_fcp_proprietary_tlv_map(tlv_map):
|
def fixup_fcp_proprietary_tlv_map(tlv_map):
|
||||||
if 'D0' in tlv_map:
|
if 'D0' in tlv_map:
|
||||||
return
|
return
|
||||||
@@ -204,6 +213,7 @@ def tlv_key_replace(inmap, indata):
|
|||||||
return key
|
return key
|
||||||
return {newkey(inmap, d[0]): d[1] for d in indata.items()}
|
return {newkey(inmap, d[0]): d[1] for d in indata.items()}
|
||||||
|
|
||||||
|
|
||||||
def tlv_val_interpret(inmap, indata):
|
def tlv_val_interpret(inmap, indata):
|
||||||
def newval(inmap, key, val):
|
def newval(inmap, key, val):
|
||||||
if key in inmap:
|
if key in inmap:
|
||||||
@@ -214,6 +224,7 @@ def tlv_val_interpret(inmap, indata):
|
|||||||
|
|
||||||
# ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4
|
# ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4
|
||||||
|
|
||||||
|
|
||||||
class _AM_DO_DF(DataObject):
|
class _AM_DO_DF(DataObject):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('access_mode', 'Access Mode', tag=0x80)
|
super().__init__('access_mode', 'Access Mode', tag=0x80)
|
||||||
@@ -262,6 +273,7 @@ class _AM_DO_DF(DataObject):
|
|||||||
|
|
||||||
class _AM_DO_EF(DataObject):
|
class _AM_DO_EF(DataObject):
|
||||||
"""ISO7816-4 9.3.2 Table 18 + 9.3.3.1 Table 31"""
|
"""ISO7816-4 9.3.2 Table 18 + 9.3.3.1 Table 31"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('access_mode', 'Access Mode', tag=0x80)
|
super().__init__('access_mode', 'Access Mode', tag=0x80)
|
||||||
|
|
||||||
@@ -306,8 +318,10 @@ class _AM_DO_EF(DataObject):
|
|||||||
val |= 0x01
|
val |= 0x01
|
||||||
return val.to_bytes(1, 'big')
|
return val.to_bytes(1, 'big')
|
||||||
|
|
||||||
|
|
||||||
class _AM_DO_CHDR(DataObject):
|
class _AM_DO_CHDR(DataObject):
|
||||||
"""Command Header Access Mode DO according to ISO 7816-4 Table 32."""
|
"""Command Header Access Mode DO according to ISO 7816-4 Table 32."""
|
||||||
|
|
||||||
def __init__(self, tag):
|
def __init__(self, tag):
|
||||||
super().__init__('command_header', 'Command Header Description', tag=tag)
|
super().__init__('command_header', 'Command Header Description', tag=tag)
|
||||||
|
|
||||||
@@ -353,6 +367,7 @@ class _AM_DO_CHDR(DataObject):
|
|||||||
res.append(self.decoded['P2'])
|
res.append(self.decoded['P2'])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
AM_DO_CHDR = DataObjectChoice('am_do_chdr', members=[
|
AM_DO_CHDR = DataObjectChoice('am_do_chdr', members=[
|
||||||
_AM_DO_CHDR(0x81), _AM_DO_CHDR(0x82), _AM_DO_CHDR(0x83), _AM_DO_CHDR(0x84),
|
_AM_DO_CHDR(0x81), _AM_DO_CHDR(0x82), _AM_DO_CHDR(0x83), _AM_DO_CHDR(0x84),
|
||||||
_AM_DO_CHDR(0x85), _AM_DO_CHDR(0x86), _AM_DO_CHDR(0x87), _AM_DO_CHDR(0x88),
|
_AM_DO_CHDR(0x85), _AM_DO_CHDR(0x86), _AM_DO_CHDR(0x87), _AM_DO_CHDR(0x88),
|
||||||
@@ -395,10 +410,13 @@ pin_names = bidict({
|
|||||||
0x8e: 'ADM10',
|
0x8e: 'ADM10',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class CRT_DO(DataObject):
|
class CRT_DO(DataObject):
|
||||||
"""Control Reference Template as per TS 102 221 9.5.1"""
|
"""Control Reference Template as per TS 102 221 9.5.1"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('control_reference_template', 'Control Reference Template', tag=0xA4)
|
super().__init__('control_reference_template',
|
||||||
|
'Control Reference Template', tag=0xA4)
|
||||||
|
|
||||||
def from_bytes(self, do: bytes):
|
def from_bytes(self, do: bytes):
|
||||||
"""Decode a Control Reference Template DO."""
|
"""Decode a Control Reference Template DO."""
|
||||||
@@ -407,7 +425,8 @@ class CRT_DO(DataObject):
|
|||||||
if do[0] != 0x83 or do[1] != 0x01:
|
if do[0] != 0x83 or do[1] != 0x01:
|
||||||
raise ValueError('Unsupported Key Ref Tag or Len in CRT DO %s', do)
|
raise ValueError('Unsupported Key Ref Tag or Len in CRT DO %s', do)
|
||||||
if do[3:] != b'\x95\x01\x08':
|
if do[3:] != b'\x95\x01\x08':
|
||||||
raise ValueError('Unsupported Usage Qualifier Tag or Len in CRT DO %s', do)
|
raise ValueError(
|
||||||
|
'Unsupported Usage Qualifier Tag or Len in CRT DO %s', do)
|
||||||
self.encoded = do[0:6]
|
self.encoded = do[0:6]
|
||||||
self.decoded = pin_names[do[2]]
|
self.decoded = pin_names[do[2]]
|
||||||
return do[6:]
|
return do[6:]
|
||||||
@@ -417,6 +436,8 @@ class CRT_DO(DataObject):
|
|||||||
return b'\x83\x01' + pin.to_bytes(1, 'big') + b'\x95\x01\x08'
|
return b'\x83\x01' + pin.to_bytes(1, 'big') + b'\x95\x01\x08'
|
||||||
|
|
||||||
# ISO7816-4 9.3.3 Table 33
|
# ISO7816-4 9.3.3 Table 33
|
||||||
|
|
||||||
|
|
||||||
class SecCondByte_DO(DataObject):
|
class SecCondByte_DO(DataObject):
|
||||||
def __init__(self, tag=0x9d):
|
def __init__(self, tag=0x9d):
|
||||||
super().__init__('security_condition_byte', tag=tag)
|
super().__init__('security_condition_byte', tag=tag)
|
||||||
@@ -470,14 +491,18 @@ class SecCondByte_DO(DataObject):
|
|||||||
raise ValueError('Unknown condition %s' % c)
|
raise ValueError('Unknown condition %s' % c)
|
||||||
return res.to_bytes(1, 'big')
|
return res.to_bytes(1, 'big')
|
||||||
|
|
||||||
|
|
||||||
Always_DO = TL0_DataObject('always', 'Always', 0x90)
|
Always_DO = TL0_DataObject('always', 'Always', 0x90)
|
||||||
Never_DO = TL0_DataObject('never', 'Never', 0x97)
|
Never_DO = TL0_DataObject('never', 'Never', 0x97)
|
||||||
|
|
||||||
|
|
||||||
class Nested_DO(DataObject):
|
class Nested_DO(DataObject):
|
||||||
"""A DO that nests another DO/Choice/Sequence"""
|
"""A DO that nests another DO/Choice/Sequence"""
|
||||||
|
|
||||||
def __init__(self, name, tag, choice):
|
def __init__(self, name, tag, choice):
|
||||||
super().__init__(name, tag=tag)
|
super().__init__(name, tag=tag)
|
||||||
self.children = choice
|
self.children = choice
|
||||||
|
|
||||||
def from_bytes(self, binary: bytes) -> list:
|
def from_bytes(self, binary: bytes) -> list:
|
||||||
remainder = binary
|
remainder = binary
|
||||||
self.decoded = []
|
self.decoded = []
|
||||||
@@ -485,10 +510,12 @@ class Nested_DO(DataObject):
|
|||||||
rc, remainder = self.children.decode(remainder)
|
rc, remainder = self.children.decode(remainder)
|
||||||
self.decoded.append(rc)
|
self.decoded.append(rc)
|
||||||
return self.decoded
|
return self.decoded
|
||||||
|
|
||||||
def to_bytes(self) -> bytes:
|
def to_bytes(self) -> bytes:
|
||||||
encoded = [self.children.encode(d) for d in self.decoded]
|
encoded = [self.children.encode(d) for d in self.decoded]
|
||||||
return b''.join(encoded)
|
return b''.join(encoded)
|
||||||
|
|
||||||
|
|
||||||
OR_Template = DataObjectChoice('or_template', 'OR-Template',
|
OR_Template = DataObjectChoice('or_template', 'OR-Template',
|
||||||
members=[Always_DO, Never_DO, SecCondByte_DO(), SecCondByte_DO(0x9e), CRT_DO()])
|
members=[Always_DO, Never_DO, SecCondByte_DO(), SecCondByte_DO(0x9e), CRT_DO()])
|
||||||
OR_DO = Nested_DO('or', 0xa0, OR_Template)
|
OR_DO = Nested_DO('or', 0xa0, OR_Template)
|
||||||
@@ -503,6 +530,8 @@ SC_DO = DataObjectChoice('security_condition', 'Security Condition',
|
|||||||
OR_DO, AND_DO, NOT_DO])
|
OR_DO, AND_DO, NOT_DO])
|
||||||
|
|
||||||
# TS 102 221 Section 13.1
|
# TS 102 221 Section 13.1
|
||||||
|
|
||||||
|
|
||||||
class EF_DIR(LinFixedEF):
|
class EF_DIR(LinFixedEF):
|
||||||
class ApplicationLabel(BER_TLV_IE, tag=0x50):
|
class ApplicationLabel(BER_TLV_IE, tag=0x50):
|
||||||
# TODO: UCS-2 coding option as per Annex A of TS 102 221
|
# TODO: UCS-2 coding option as per Annex A of TS 102 221
|
||||||
@@ -522,6 +551,8 @@ class EF_DIR(LinFixedEF):
|
|||||||
self._tlv = EF_DIR.ApplicationTemplate
|
self._tlv = EF_DIR.ApplicationTemplate
|
||||||
|
|
||||||
# TS 102 221 Section 13.2
|
# TS 102 221 Section 13.2
|
||||||
|
|
||||||
|
|
||||||
class EF_ICCID(TransparentEF):
|
class EF_ICCID(TransparentEF):
|
||||||
def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'):
|
def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size={10, 10})
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size={10, 10})
|
||||||
@@ -533,14 +564,19 @@ class EF_ICCID(TransparentEF):
|
|||||||
return enc_iccid(abstract['iccid'])
|
return enc_iccid(abstract['iccid'])
|
||||||
|
|
||||||
# TS 102 221 Section 13.3
|
# TS 102 221 Section 13.3
|
||||||
|
|
||||||
|
|
||||||
class EF_PL(TransRecEF):
|
class EF_PL(TransRecEF):
|
||||||
def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'):
|
def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=2, size={2,None})
|
super().__init__(fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len=2, size={2, None})
|
||||||
|
|
||||||
def _decode_record_bin(self, bin_data):
|
def _decode_record_bin(self, bin_data):
|
||||||
if bin_data == b'\xff\xff':
|
if bin_data == b'\xff\xff':
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return bin_data.decode('ascii')
|
return bin_data.decode('ascii')
|
||||||
|
|
||||||
def _encode_record_bin(self, in_json):
|
def _encode_record_bin(self, in_json):
|
||||||
if in_json is None:
|
if in_json is None:
|
||||||
return b'\xff\xff'
|
return b'\xff\xff'
|
||||||
@@ -629,8 +665,11 @@ class EF_ARR(LinFixedEF):
|
|||||||
class EF_UMPC(TransparentEF):
|
class EF_UMPC(TransparentEF):
|
||||||
def __init__(self, fid='2f08', sfid=0x08, name='EF.UMPC', desc='UICC Maximum Power Consumption'):
|
def __init__(self, fid='2f08', sfid=0x08, name='EF.UMPC', desc='UICC Maximum Power Consumption'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size={5, 5})
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size={5, 5})
|
||||||
addl_info = FlagsEnum(Byte, req_inc_idle_current=1, support_uicc_suspend=2)
|
addl_info = FlagsEnum(Byte, req_inc_idle_current=1,
|
||||||
self._construct = Struct('max_current_mA'/Int8ub, 't_op_s'/Int8ub, 'addl_info'/addl_info)
|
support_uicc_suspend=2)
|
||||||
|
self._construct = Struct(
|
||||||
|
'max_current_mA'/Int8ub, 't_op_s'/Int8ub, 'addl_info'/addl_info)
|
||||||
|
|
||||||
|
|
||||||
class CardProfileUICC(CardProfile):
|
class CardProfileUICC(CardProfile):
|
||||||
|
|
||||||
@@ -714,7 +753,8 @@ class CardProfileUICC(CardProfile):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
super().__init__(name, desc='ETSI TS 102 221', cla="00", sel_ctrl="0004", files_in_mf=files, sw=sw)
|
super().__init__(name, desc='ETSI TS 102 221', cla="00",
|
||||||
|
sel_ctrl="0004", files_in_mf=files, sw=sw)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode_select_response(resp_hex: str) -> object:
|
def decode_select_response(resp_hex: str) -> object:
|
||||||
@@ -741,6 +781,7 @@ class CardProfileUICC(CardProfile):
|
|||||||
def match_with_card(scc: SimCardCommands) -> bool:
|
def match_with_card(scc: SimCardCommands) -> bool:
|
||||||
return match_uicc(scc)
|
return match_uicc(scc)
|
||||||
|
|
||||||
|
|
||||||
class CardProfileUICCSIM(CardProfileUICC):
|
class CardProfileUICCSIM(CardProfileUICC):
|
||||||
"""Same as above, but including 2G SIM support"""
|
"""Same as above, but including 2G SIM support"""
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,21 @@ Various constants from 3GPP TS 31.102 V16.6.0
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Mapping between USIM Service Number and its description
|
# Mapping between USIM Service Number and its description
|
||||||
|
import pySim.ts_102_221
|
||||||
|
from pySim.ts_51_011 import EF_ACMmax, EF_AAeM, EF_eMLPP, EF_CMI, EF_PNN
|
||||||
|
from pySim.ts_51_011 import EF_MMSN, EF_MMSICP, EF_MMSUP, EF_MMSUCP, EF_VGCS, EF_VGCSS, EF_NIA
|
||||||
|
from pySim.ts_51_011 import EF_SMSR, EF_DCK, EF_EXT, EF_CNL, EF_OPL, EF_MBI, EF_MWIS
|
||||||
|
from pySim.ts_51_011 import EF_CBMID, EF_CBMIR, EF_ADN, EF_SMS, EF_MSISDN, EF_SMSP, EF_SMSS
|
||||||
|
from pySim.ts_51_011 import EF_IMSI, EF_xPLMNwAcT, EF_SPN, EF_CBMI, EF_ACC, EF_PLMNsel
|
||||||
|
from pySim.ts_102_221 import EF_ARR
|
||||||
|
from pySim.tlv import *
|
||||||
|
from pySim.filesystem import *
|
||||||
|
from pySim.construct import *
|
||||||
|
from construct import Optional as COptional
|
||||||
|
from construct import *
|
||||||
|
from typing import Tuple
|
||||||
|
from struct import unpack, pack
|
||||||
|
import enum
|
||||||
EF_UST_map = {
|
EF_UST_map = {
|
||||||
1: 'Local Phone Book',
|
1: 'Local Phone Book',
|
||||||
2: 'Fixed Dialling Numbers (FDN)',
|
2: 'Fixed Dialling Numbers (FDN)',
|
||||||
@@ -287,24 +302,9 @@ EF_USIM_ADF_map = {
|
|||||||
# ADF.USIM
|
# ADF.USIM
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
import enum
|
|
||||||
from struct import unpack, pack
|
|
||||||
from typing import Tuple
|
|
||||||
from construct import *
|
|
||||||
from construct import Optional as COptional
|
|
||||||
from pySim.construct import *
|
|
||||||
from pySim.filesystem import *
|
|
||||||
from pySim.tlv import *
|
|
||||||
from pySim.ts_102_221 import EF_ARR
|
|
||||||
from pySim.ts_51_011 import EF_IMSI, EF_xPLMNwAcT, EF_SPN, EF_CBMI, EF_ACC, EF_PLMNsel
|
|
||||||
from pySim.ts_51_011 import EF_CBMID, EF_CBMIR, EF_ADN, EF_SMS, EF_MSISDN, EF_SMSP, EF_SMSS
|
|
||||||
from pySim.ts_51_011 import EF_SMSR, EF_DCK, EF_EXT, EF_CNL, EF_OPL, EF_MBI, EF_MWIS
|
|
||||||
from pySim.ts_51_011 import EF_MMSN, EF_MMSICP, EF_MMSUP, EF_MMSUCP, EF_VGCS, EF_VGCSS, EF_NIA
|
|
||||||
from pySim.ts_51_011 import EF_ACMmax, EF_AAeM, EF_eMLPP, EF_CMI, EF_PNN
|
|
||||||
|
|
||||||
import pySim.ts_102_221
|
|
||||||
|
|
||||||
# 3GPP TS 31.102 Section 4.4.11.4 (EF_5GS3GPPNSC)
|
# 3GPP TS 31.102 Section 4.4.11.4 (EF_5GS3GPPNSC)
|
||||||
|
|
||||||
class EF_5GS3GPPNSC(LinFixedEF):
|
class EF_5GS3GPPNSC(LinFixedEF):
|
||||||
class NgKSI(BER_TLV_IE, tag=0x80):
|
class NgKSI(BER_TLV_IE, tag=0x80):
|
||||||
_construct = Int8ub
|
_construct = Int8ub
|
||||||
@@ -338,6 +338,8 @@ class EF_5GS3GPPNSC(LinFixedEF):
|
|||||||
self._tlv = EF_5GS3GPPNSC.FiveGSNasSecurityContext
|
self._tlv = EF_5GS3GPPNSC.FiveGSNasSecurityContext
|
||||||
|
|
||||||
# 3GPP TS 31.102 Section 4.4.11.6
|
# 3GPP TS 31.102 Section 4.4.11.6
|
||||||
|
|
||||||
|
|
||||||
class EF_5GAUTHKEYS(TransparentEF):
|
class EF_5GAUTHKEYS(TransparentEF):
|
||||||
class K_AUSF(BER_TLV_IE, tag=0x80):
|
class K_AUSF(BER_TLV_IE, tag=0x80):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
@@ -354,24 +356,32 @@ class EF_5GAUTHKEYS(TransparentEF):
|
|||||||
self._tlv = EF_5GAUTHKEYS.FiveGAuthKeys
|
self._tlv = EF_5GAUTHKEYS.FiveGAuthKeys
|
||||||
|
|
||||||
# 3GPP TS 31.102 Section 4.4.11.8
|
# 3GPP TS 31.102 Section 4.4.11.8
|
||||||
|
|
||||||
|
|
||||||
class ProtSchemeIdList(BER_TLV_IE, tag=0xa0):
|
class ProtSchemeIdList(BER_TLV_IE, tag=0xa0):
|
||||||
# FIXME: 3GPP TS 24.501 Protection Scheme Identifier
|
# FIXME: 3GPP TS 24.501 Protection Scheme Identifier
|
||||||
# repeated sequence of (id, index) tuples
|
# repeated sequence of (id, index) tuples
|
||||||
_construct = GreedyRange(Struct('id'/Enum(Byte, null=0, A=1, B=2), 'index'/Int8ub))
|
_construct = GreedyRange(
|
||||||
|
Struct('id'/Enum(Byte, null=0, A=1, B=2), 'index'/Int8ub))
|
||||||
|
|
||||||
|
|
||||||
class HomeNetPubKeyId(BER_TLV_IE, tag=0x80):
|
class HomeNetPubKeyId(BER_TLV_IE, tag=0x80):
|
||||||
# 3GPP TS 24.501 / 3GPP TS 23.003
|
# 3GPP TS 24.501 / 3GPP TS 23.003
|
||||||
_construct = Int8ub
|
_construct = Int8ub
|
||||||
|
|
||||||
|
|
||||||
class HomeNetPubKey(BER_TLV_IE, tag=0x81):
|
class HomeNetPubKey(BER_TLV_IE, tag=0x81):
|
||||||
# FIXME: RFC 5480
|
# FIXME: RFC 5480
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
|
|
||||||
class HomeNetPubKeyList(BER_TLV_IE, tag=0xa1,
|
class HomeNetPubKeyList(BER_TLV_IE, tag=0xa1,
|
||||||
nested=[HomeNetPubKeyId, HomeNetPubKey]):
|
nested=[HomeNetPubKeyId, HomeNetPubKey]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 3GPP TS 31.102 Section 4.4.11.6
|
# 3GPP TS 31.102 Section 4.4.11.6
|
||||||
|
|
||||||
|
|
||||||
class SUCI_CalcInfo(TLV_IE_Collection, nested=[ProtSchemeIdList, HomeNetPubKeyList]):
|
class SUCI_CalcInfo(TLV_IE_Collection, nested=[ProtSchemeIdList, HomeNetPubKeyList]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -414,7 +424,8 @@ class EF_SUCI_Calc_Info(TransparentEF):
|
|||||||
return out_bytes
|
return out_bytes
|
||||||
|
|
||||||
def _encode_hex(self, in_json):
|
def _encode_hex(self, in_json):
|
||||||
out_bytes = self._encode_prot_scheme_id_list(in_json['prot_scheme_id_list'])
|
out_bytes = self._encode_prot_scheme_id_list(
|
||||||
|
in_json['prot_scheme_id_list'])
|
||||||
out_bytes += self._encode_hnet_pubkey_list(in_json['hnet_pubkey_list'])
|
out_bytes += self._encode_hnet_pubkey_list(in_json['hnet_pubkey_list'])
|
||||||
return "".join(["%02X" % i for i in out_bytes])
|
return "".join(["%02X" % i for i in out_bytes])
|
||||||
|
|
||||||
@@ -447,7 +458,8 @@ class EF_SUCI_Calc_Info(TransparentEF):
|
|||||||
print("missing Home Network Public Key Identifier tag")
|
print("missing Home Network Public Key Identifier tag")
|
||||||
return {}
|
return {}
|
||||||
pos += 1
|
pos += 1
|
||||||
hnet_pubkey_id_len = in_bytes[pos] # TODO might be more than 1 byte?
|
# TODO might be more than 1 byte?
|
||||||
|
hnet_pubkey_id_len = in_bytes[pos]
|
||||||
pos += 1
|
pos += 1
|
||||||
hnet_pubkey_id = in_bytes[pos:pos+hnet_pubkey_id_len][0]
|
hnet_pubkey_id = in_bytes[pos:pos+hnet_pubkey_id_len][0]
|
||||||
pos += hnet_pubkey_id_len
|
pos += hnet_pubkey_id_len
|
||||||
@@ -482,7 +494,8 @@ class EF_SUCI_Calc_Info(TransparentEF):
|
|||||||
prot_scheme_id_list_len = in_bytes[pos] # TODO maybe more than 1 byte
|
prot_scheme_id_list_len = in_bytes[pos] # TODO maybe more than 1 byte
|
||||||
pos += 1
|
pos += 1
|
||||||
# decode Protection Scheme Identifier List data object
|
# decode Protection Scheme Identifier List data object
|
||||||
prot_scheme_id_list = self._decode_prot_scheme_id_list(in_bytes[pos:pos+prot_scheme_id_list_len])
|
prot_scheme_id_list = self._decode_prot_scheme_id_list(
|
||||||
|
in_bytes[pos:pos+prot_scheme_id_list_len])
|
||||||
pos += prot_scheme_id_list_len
|
pos += prot_scheme_id_list_len
|
||||||
|
|
||||||
# remaining data holds Home Network Public Key Data Object
|
# remaining data holds Home Network Public Key Data Object
|
||||||
@@ -496,16 +509,19 @@ class EF_SUCI_Calc_Info(TransparentEF):
|
|||||||
def _encode_bin(self, in_json):
|
def _encode_bin(self, in_json):
|
||||||
return h2b(self._encode_hex(in_json))
|
return h2b(self._encode_hex(in_json))
|
||||||
|
|
||||||
|
|
||||||
class EF_LI(TransRecEF):
|
class EF_LI(TransRecEF):
|
||||||
def __init__(self, fid='6f05', sfid=None, name='EF.LI', size={2, None}, rec_len=2,
|
def __init__(self, fid='6f05', sfid=None, name='EF.LI', size={2, None}, rec_len=2,
|
||||||
desc='Language Indication'):
|
desc='Language Indication'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
||||||
|
|
||||||
def _decode_record_bin(self, in_bin):
|
def _decode_record_bin(self, in_bin):
|
||||||
if in_bin == b'\xff\xff':
|
if in_bin == b'\xff\xff':
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# officially this is 7-bit GSM alphabet with one padding bit in each byte
|
# officially this is 7-bit GSM alphabet with one padding bit in each byte
|
||||||
return in_bin.decode('ascii')
|
return in_bin.decode('ascii')
|
||||||
|
|
||||||
def _encode_record_bin(self, in_json):
|
def _encode_record_bin(self, in_json):
|
||||||
if in_json == None:
|
if in_json == None:
|
||||||
return b'\xff\xff'
|
return b'\xff\xff'
|
||||||
@@ -513,13 +529,17 @@ class EF_LI(TransRecEF):
|
|||||||
# officially this is 7-bit GSM alphabet with one padding bit in each byte
|
# officially this is 7-bit GSM alphabet with one padding bit in each byte
|
||||||
return in_json.encode('ascii')
|
return in_json.encode('ascii')
|
||||||
|
|
||||||
|
|
||||||
class EF_Keys(TransparentEF):
|
class EF_Keys(TransparentEF):
|
||||||
def __init__(self, fid='6f08', sfid=0x08, name='EF.Keys', size={33, 33},
|
def __init__(self, fid='6f08', sfid=0x08, name='EF.Keys', size={33, 33},
|
||||||
desc='Ciphering and Integrity Keys'):
|
desc='Ciphering and Integrity Keys'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self._construct = Struct('ksi'/Int8ub, 'ck'/HexAdapter(Bytes(16)), 'ik'/HexAdapter(Bytes(16)))
|
self._construct = Struct(
|
||||||
|
'ksi'/Int8ub, 'ck'/HexAdapter(Bytes(16)), 'ik'/HexAdapter(Bytes(16)))
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.6
|
# TS 31.102 Section 4.2.6
|
||||||
|
|
||||||
|
|
||||||
class EF_HPPLMN(TransparentEF):
|
class EF_HPPLMN(TransparentEF):
|
||||||
def __init__(self, fid='6f31', sfid=0x12, name='EF.HPPLMN', size={1, 1},
|
def __init__(self, fid='6f31', sfid=0x12, name='EF.HPPLMN', size={1, 1},
|
||||||
desc='Higher Priority PLMN search period'):
|
desc='Higher Priority PLMN search period'):
|
||||||
@@ -527,18 +547,22 @@ class EF_HPPLMN(TransparentEF):
|
|||||||
self._construct = Int8ub
|
self._construct = Int8ub
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.8
|
# TS 31.102 Section 4.2.8
|
||||||
|
|
||||||
|
|
||||||
class EF_UServiceTable(TransparentEF):
|
class EF_UServiceTable(TransparentEF):
|
||||||
def __init__(self, fid, sfid, name, desc, size, table):
|
def __init__(self, fid, sfid, name, desc, size, table):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self.table = table
|
self.table = table
|
||||||
# add those commands to the general commands of a TransparentEF
|
# add those commands to the general commands of a TransparentEF
|
||||||
self.shell_commands += [self.AddlShellCommands()]
|
self.shell_commands += [self.AddlShellCommands()]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]:
|
def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]:
|
||||||
i = service - 1
|
i = service - 1
|
||||||
byte_offset = i//8
|
byte_offset = i//8
|
||||||
bit_offset = (i % 8)
|
bit_offset = (i % 8)
|
||||||
return (byte_offset, bit_offset)
|
return (byte_offset, bit_offset)
|
||||||
|
|
||||||
def _decode_bin(self, in_bin):
|
def _decode_bin(self, in_bin):
|
||||||
ret = {}
|
ret = {}
|
||||||
for i in range(0, len(in_bin)):
|
for i in range(0, len(in_bin)):
|
||||||
@@ -551,25 +575,29 @@ class EF_UServiceTable(TransparentEF):
|
|||||||
if service_nr in self.table:
|
if service_nr in self.table:
|
||||||
ret[service_nr]['description'] = self.table[service_nr]
|
ret[service_nr]['description'] = self.table[service_nr]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _encode_bin(self, in_json):
|
def _encode_bin(self, in_json):
|
||||||
# compute the required binary size
|
# compute the required binary size
|
||||||
bin_len = 0
|
bin_len = 0
|
||||||
for srv in in_json.keys():
|
for srv in in_json.keys():
|
||||||
service_nr = int(srv)
|
service_nr = int(srv)
|
||||||
(byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service(service_nr)
|
(byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service(
|
||||||
|
service_nr)
|
||||||
if byte_offset >= bin_len:
|
if byte_offset >= bin_len:
|
||||||
bin_len = byte_offset+1
|
bin_len = byte_offset+1
|
||||||
# encode the actual data
|
# encode the actual data
|
||||||
out = bytearray(b'\x00' * bin_len)
|
out = bytearray(b'\x00' * bin_len)
|
||||||
for srv in in_json.keys():
|
for srv in in_json.keys():
|
||||||
service_nr = int(srv)
|
service_nr = int(srv)
|
||||||
(byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service(service_nr)
|
(byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service(
|
||||||
|
service_nr)
|
||||||
if in_json[srv]['activated'] == True:
|
if in_json[srv]['activated'] == True:
|
||||||
bit = 1
|
bit = 1
|
||||||
else:
|
else:
|
||||||
bit = 0
|
bit = 0
|
||||||
out[byte_offset] |= (bit) << bit_offset
|
out[byte_offset] |= (bit) << bit_offset
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@with_default_category('File-Specific Commands')
|
@with_default_category('File-Specific Commands')
|
||||||
class AddlShellCommands(CommandSet):
|
class AddlShellCommands(CommandSet):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -584,14 +612,18 @@ class EF_UServiceTable(TransparentEF):
|
|||||||
self._cmd.card.update_ust(int(arg), 0)
|
self._cmd.card.update_ust(int(arg), 0)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.7 - *not* the same as DF.GSM/EF.ECC!
|
# TS 31.103 Section 4.2.7 - *not* the same as DF.GSM/EF.ECC!
|
||||||
|
|
||||||
|
|
||||||
class EF_ECC(LinFixedEF):
|
class EF_ECC(LinFixedEF):
|
||||||
cc_construct = Rpad(BcdAdapter(Rpad(Bytes(3))), pattern='f')
|
cc_construct = Rpad(BcdAdapter(Rpad(Bytes(3))), pattern='f')
|
||||||
category_construct = FlagsEnum(Byte, police=1, ambulance=2, fire_brigade=3, marine_guard=4,
|
category_construct = FlagsEnum(Byte, police=1, ambulance=2, fire_brigade=3, marine_guard=4,
|
||||||
mountain_rescue=5, manual_ecall=6, automatic_ecall=7)
|
mountain_rescue=5, manual_ecall=6, automatic_ecall=7)
|
||||||
alpha_construct = GsmStringAdapter(Rpad(GreedyBytes))
|
alpha_construct = GsmStringAdapter(Rpad(GreedyBytes))
|
||||||
|
|
||||||
def __init__(self, fid='6fb7', sfid=0x01, name='EF.ECC',
|
def __init__(self, fid='6fb7', sfid=0x01, name='EF.ECC',
|
||||||
desc='Emergency Call Codes'):
|
desc='Emergency Call Codes'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={4, 20})
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={4, 20})
|
||||||
|
|
||||||
def _decode_record_bin(self, in_bin):
|
def _decode_record_bin(self, in_bin):
|
||||||
# mandatory parts
|
# mandatory parts
|
||||||
code = in_bin[:3]
|
code = in_bin[:3]
|
||||||
@@ -605,6 +637,7 @@ class EF_ECC(LinFixedEF):
|
|||||||
alpha_id = in_bin[3:-1]
|
alpha_id = in_bin[3:-1]
|
||||||
ret['alpha_id'] = parse_construct(EF_ECC.alpha_construct, alpha_id)
|
ret['alpha_id'] = parse_construct(EF_ECC.alpha_construct, alpha_id)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _encode_record_bin(self, in_json):
|
def _encode_record_bin(self, in_json):
|
||||||
if in_json is None:
|
if in_json is None:
|
||||||
return b'\xff\xff\xff\xff'
|
return b'\xff\xff\xff\xff'
|
||||||
@@ -622,6 +655,8 @@ class EF_LOCI(TransparentEF):
|
|||||||
self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/HexAdapter(Bytes(5)), 'rfu'/Int8ub,
|
self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/HexAdapter(Bytes(5)), 'rfu'/Int8ub,
|
||||||
'lu_status'/Int8ub)
|
'lu_status'/Int8ub)
|
||||||
# TS 31.102 Section 4.2.18
|
# TS 31.102 Section 4.2.18
|
||||||
|
|
||||||
|
|
||||||
class EF_AD(TransparentEF):
|
class EF_AD(TransparentEF):
|
||||||
class OP_MODE(enum.IntEnum):
|
class OP_MODE(enum.IntEnum):
|
||||||
normal = 0x00
|
normal = 0x00
|
||||||
@@ -645,6 +680,8 @@ class EF_AD(TransparentEF):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.23
|
# TS 31.102 Section 4.2.23
|
||||||
|
|
||||||
|
|
||||||
class EF_PSLOCI(TransparentEF):
|
class EF_PSLOCI(TransparentEF):
|
||||||
def __init__(self, fid='6f73', sfid=0x0c, name='EF.PSLOCI', desc='PS Location information', size={14, 14}):
|
def __init__(self, fid='6f73', sfid=0x0c, name='EF.PSLOCI', desc='PS Location information', size={14, 14}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
@@ -652,6 +689,8 @@ class EF_PSLOCI(TransparentEF):
|
|||||||
'rai'/HexAdapter(Bytes(6)), 'rau_status'/Int8ub)
|
'rai'/HexAdapter(Bytes(6)), 'rau_status'/Int8ub)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.33
|
# TS 31.102 Section 4.2.33
|
||||||
|
|
||||||
|
|
||||||
class EF_ICI(CyclicEF):
|
class EF_ICI(CyclicEF):
|
||||||
def __init__(self, fid='6f80', sfid=0x14, name='EF.ICI', rec_len={28, 48},
|
def __init__(self, fid='6f80', sfid=0x14, name='EF.ICI', rec_len={28, 48},
|
||||||
desc='Incoming Call Information'):
|
desc='Incoming Call Information'):
|
||||||
@@ -668,6 +707,8 @@ class EF_ICI(CyclicEF):
|
|||||||
'link_to_phonebook'/Bytes(3))
|
'link_to_phonebook'/Bytes(3))
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.34
|
# TS 31.102 Section 4.2.34
|
||||||
|
|
||||||
|
|
||||||
class EF_OCI(CyclicEF):
|
class EF_OCI(CyclicEF):
|
||||||
def __init__(self, fid='6f81', sfid=0x15, name='EF.OCI', rec_len={27, 47},
|
def __init__(self, fid='6f81', sfid=0x15, name='EF.OCI', rec_len={27, 47},
|
||||||
desc='Outgoing Call Information'):
|
desc='Outgoing Call Information'):
|
||||||
@@ -683,6 +724,8 @@ class EF_OCI(CyclicEF):
|
|||||||
'link_to_phonebook'/Bytes(3))
|
'link_to_phonebook'/Bytes(3))
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.35
|
# TS 31.102 Section 4.2.35
|
||||||
|
|
||||||
|
|
||||||
class EF_ICT(CyclicEF):
|
class EF_ICT(CyclicEF):
|
||||||
def __init__(self, fid='6f82', sfid=None, name='EF.ICT', rec_len={3, 3},
|
def __init__(self, fid='6f82', sfid=None, name='EF.ICT', rec_len={3, 3},
|
||||||
desc='Incoming Call Timer'):
|
desc='Incoming Call Timer'):
|
||||||
@@ -690,11 +733,16 @@ class EF_ICT(CyclicEF):
|
|||||||
self._construct = Struct('accumulated_call_timer'/Int24ub)
|
self._construct = Struct('accumulated_call_timer'/Int24ub)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.38
|
# TS 31.102 Section 4.2.38
|
||||||
|
|
||||||
|
|
||||||
class EF_CCP2(LinFixedEF):
|
class EF_CCP2(LinFixedEF):
|
||||||
def __init__(self, fid='6f4f', sfid=0x16, name='EF.CCP2', desc='Capability Configuration Parameters 2'):
|
def __init__(self, fid='6f4f', sfid=0x16, name='EF.CCP2', desc='Capability Configuration Parameters 2'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={15,None})
|
super().__init__(fid=fid, sfid=sfid,
|
||||||
|
name=name, desc=desc, rec_len={15, None})
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.48
|
# TS 31.102 Section 4.2.48
|
||||||
|
|
||||||
|
|
||||||
class EF_ACL(TransparentEF):
|
class EF_ACL(TransparentEF):
|
||||||
def __init__(self, fid='6f57', sfid=None, name='EF.ACL', size={32, None},
|
def __init__(self, fid='6f57', sfid=None, name='EF.ACL', size={32, None},
|
||||||
desc='Access Point Name Control List'):
|
desc='Access Point Name Control List'):
|
||||||
@@ -702,6 +750,8 @@ class EF_ACL(TransparentEF):
|
|||||||
self._construct = Struct('num_of_apns'/Int8ub, 'tlvs'/GreedyBytes)
|
self._construct = Struct('num_of_apns'/Int8ub, 'tlvs'/GreedyBytes)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.51
|
# TS 31.102 Section 4.2.51
|
||||||
|
|
||||||
|
|
||||||
class EF_START_HFN(TransparentEF):
|
class EF_START_HFN(TransparentEF):
|
||||||
def __init__(self, fid='6f5b', sfid=0x0f, name='EF.START-HFN', size={6, 6},
|
def __init__(self, fid='6f5b', sfid=0x0f, name='EF.START-HFN', size={6, 6},
|
||||||
desc='Initialisation values for Hyperframe number'):
|
desc='Initialisation values for Hyperframe number'):
|
||||||
@@ -709,6 +759,8 @@ class EF_START_HFN(TransparentEF):
|
|||||||
self._construct = Struct('start_cs'/Int24ub, 'start_ps'/Int24ub)
|
self._construct = Struct('start_cs'/Int24ub, 'start_ps'/Int24ub)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.52
|
# TS 31.102 Section 4.2.52
|
||||||
|
|
||||||
|
|
||||||
class EF_THRESHOLD(TransparentEF):
|
class EF_THRESHOLD(TransparentEF):
|
||||||
def __init__(self, fid='6f5c', sfid=0x10, name='EF.THRESHOLD', size={3, 3},
|
def __init__(self, fid='6f5c', sfid=0x10, name='EF.THRESHOLD', size={3, 3},
|
||||||
desc='Maximum value of START'):
|
desc='Maximum value of START'):
|
||||||
@@ -716,6 +768,8 @@ class EF_THRESHOLD(TransparentEF):
|
|||||||
self._construct = Struct('max_start'/Int24ub)
|
self._construct = Struct('max_start'/Int24ub)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.77
|
# TS 31.102 Section 4.2.77
|
||||||
|
|
||||||
|
|
||||||
class EF_VGCSCA(TransRecEF):
|
class EF_VGCSCA(TransRecEF):
|
||||||
def __init__(self, fid='6fd4', sfid=None, name='EF.VGCSCA', size={2, 100}, rec_len=2,
|
def __init__(self, fid='6fd4', sfid=None, name='EF.VGCSCA', size={2, 100}, rec_len=2,
|
||||||
desc='Voice Group Call Service Ciphering Algorithm'):
|
desc='Voice Group Call Service Ciphering Algorithm'):
|
||||||
@@ -723,6 +777,8 @@ class EF_VGCSCA(TransRecEF):
|
|||||||
self._construct = Struct('alg_v_ki_1'/Int8ub, 'alg_v_ki_2'/Int8ub)
|
self._construct = Struct('alg_v_ki_1'/Int8ub, 'alg_v_ki_2'/Int8ub)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.79
|
# TS 31.102 Section 4.2.79
|
||||||
|
|
||||||
|
|
||||||
class EF_GBABP(TransparentEF):
|
class EF_GBABP(TransparentEF):
|
||||||
def __init__(self, fid='6fd6', sfid=None, name='EF.GBABP', size={3, 50},
|
def __init__(self, fid='6fd6', sfid=None, name='EF.GBABP', size={3, 50},
|
||||||
desc='GBA Bootstrapping parameters'):
|
desc='GBA Bootstrapping parameters'):
|
||||||
@@ -730,42 +786,61 @@ class EF_GBABP(TransparentEF):
|
|||||||
self._construct = Struct('rand'/LV, 'b_tid'/LV, 'key_lifetime'/LV)
|
self._construct = Struct('rand'/LV, 'b_tid'/LV, 'key_lifetime'/LV)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.80
|
# TS 31.102 Section 4.2.80
|
||||||
|
|
||||||
|
|
||||||
class EF_MSK(LinFixedEF):
|
class EF_MSK(LinFixedEF):
|
||||||
def __init__(self, fid='6fd7', sfid=None, name='EF.MSK', desc='MBMS Service Key List'):
|
def __init__(self, fid='6fd7', sfid=None, name='EF.MSK', desc='MBMS Service Key List'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={20,None})
|
super().__init__(fid=fid, sfid=sfid,
|
||||||
|
name=name, desc=desc, rec_len={20, None})
|
||||||
msk_ts_constr = Struct('msk_id'/Int32ub, 'timestamp_counter'/Int32ub)
|
msk_ts_constr = Struct('msk_id'/Int32ub, 'timestamp_counter'/Int32ub)
|
||||||
self._construct = Struct('key_domain_id'/Bytes(3),
|
self._construct = Struct('key_domain_id'/Bytes(3),
|
||||||
'num_msk_id'/Int8ub,
|
'num_msk_id'/Int8ub,
|
||||||
'msk_ids'/msk_ts_constr[this.num_msk_id])
|
'msk_ids'/msk_ts_constr[this.num_msk_id])
|
||||||
# TS 31.102 Section 4.2.81
|
# TS 31.102 Section 4.2.81
|
||||||
|
|
||||||
|
|
||||||
class EF_MUK(LinFixedEF):
|
class EF_MUK(LinFixedEF):
|
||||||
class MUK_Idr(BER_TLV_IE, tag=0x80):
|
class MUK_Idr(BER_TLV_IE, tag=0x80):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class MUK_Idi(BER_TLV_IE, tag=0x82):
|
class MUK_Idi(BER_TLV_IE, tag=0x82):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class MUK_ID(BER_TLV_IE, tag=0xA0, nested=[MUK_Idr, MUK_Idi]):
|
class MUK_ID(BER_TLV_IE, tag=0xA0, nested=[MUK_Idr, MUK_Idi]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class TimeStampCounter(BER_TLV_IE, tag=0x81):
|
class TimeStampCounter(BER_TLV_IE, tag=0x81):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class EF_MUK_Collection(TLV_IE_Collection, nested=[MUK_ID, TimeStampCounter]):
|
class EF_MUK_Collection(TLV_IE_Collection, nested=[MUK_ID, TimeStampCounter]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, fid='6fd8', sfid=None, name='EF.MUK', desc='MBMS User Key'):
|
def __init__(self, fid='6fd8', sfid=None, name='EF.MUK', desc='MBMS User Key'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={None,None})
|
super().__init__(fid=fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len={None, None})
|
||||||
self._tlv = EF_MUK.EF_MUK_Collection
|
self._tlv = EF_MUK.EF_MUK_Collection
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.83
|
# TS 31.102 Section 4.2.83
|
||||||
|
|
||||||
|
|
||||||
class EF_GBANL(LinFixedEF):
|
class EF_GBANL(LinFixedEF):
|
||||||
class NAF_ID(BER_TLV_IE, tag=0x80):
|
class NAF_ID(BER_TLV_IE, tag=0x80):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class B_TID(BER_TLV_IE, tag=0x81):
|
class B_TID(BER_TLV_IE, tag=0x81):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class EF_GBANL_Collection(BER_TLV_IE, nested=[NAF_ID, B_TID]):
|
class EF_GBANL_Collection(BER_TLV_IE, nested=[NAF_ID, B_TID]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, fid='6fda', sfid=None, name='EF.GBANL', desc='GBA NAF List'):
|
def __init__(self, fid='6fda', sfid=None, name='EF.GBANL', desc='GBA NAF List'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={None,None})
|
super().__init__(fid=fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len={None, None})
|
||||||
self._tlv = EF_GBANL.EF_GBANL_Collection
|
self._tlv = EF_GBANL.EF_GBANL_Collection
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.85
|
# TS 31.102 Section 4.2.85
|
||||||
|
|
||||||
|
|
||||||
class EF_EHPLMNPI(TransparentEF):
|
class EF_EHPLMNPI(TransparentEF):
|
||||||
def __init__(self, fid='6fdb', sfid=None, name='EF.EHPLMNPI', size={1, 1},
|
def __init__(self, fid='6fdb', sfid=None, name='EF.EHPLMNPI', size={1, 1},
|
||||||
desc='Equivalent HPLMN Presentation Indication'):
|
desc='Equivalent HPLMN Presentation Indication'):
|
||||||
@@ -774,6 +849,8 @@ class EF_EHPLMNPI(TransparentEF):
|
|||||||
Enum(Byte, no_preference=0, display_highest_prio_only=1, display_all=2))
|
Enum(Byte, no_preference=0, display_highest_prio_only=1, display_all=2))
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.87
|
# TS 31.102 Section 4.2.87
|
||||||
|
|
||||||
|
|
||||||
class EF_NAFKCA(LinFixedEF):
|
class EF_NAFKCA(LinFixedEF):
|
||||||
class NAF_KeyCentreAddress(BER_TLV_IE, tag=0x80):
|
class NAF_KeyCentreAddress(BER_TLV_IE, tag=0x80):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
@@ -783,23 +860,30 @@ class EF_NAFKCA(LinFixedEF):
|
|||||||
self._tlv = EF_NAFKCA.NAF_KeyCentreAddress
|
self._tlv = EF_NAFKCA.NAF_KeyCentreAddress
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.90
|
# TS 31.102 Section 4.2.90
|
||||||
|
|
||||||
|
|
||||||
class EF_NCP_IP(LinFixedEF):
|
class EF_NCP_IP(LinFixedEF):
|
||||||
class DataDestAddrRange(TLV_IE, tag=0x83):
|
class DataDestAddrRange(TLV_IE, tag=0x83):
|
||||||
_construct = Struct('type_of_address'/Enum(Byte, IPv4=0x21, IPv6=0x56),
|
_construct = Struct('type_of_address'/Enum(Byte, IPv4=0x21, IPv6=0x56),
|
||||||
'prefix_length'/Int8ub,
|
'prefix_length'/Int8ub,
|
||||||
'prefix'/HexAdapter(GreedyBytes))
|
'prefix'/HexAdapter(GreedyBytes))
|
||||||
|
|
||||||
class AccessPointName(TLV_IE, tag=0x80):
|
class AccessPointName(TLV_IE, tag=0x80):
|
||||||
# coded as per TS 23.003
|
# coded as per TS 23.003
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class Login(TLV_IE, tag=0x81):
|
class Login(TLV_IE, tag=0x81):
|
||||||
# as per SMS DCS TS 23.038
|
# as per SMS DCS TS 23.038
|
||||||
_construct = GsmStringAdapter(GreedyBytes)
|
_construct = GsmStringAdapter(GreedyBytes)
|
||||||
|
|
||||||
class Password(TLV_IE, tag=0x82):
|
class Password(TLV_IE, tag=0x82):
|
||||||
# as per SMS DCS TS 23.038
|
# as per SMS DCS TS 23.038
|
||||||
_construct = GsmStringAdapter(GreedyBytes)
|
_construct = GsmStringAdapter(GreedyBytes)
|
||||||
|
|
||||||
class BearerDescription(TLV_IE, tag=0x84):
|
class BearerDescription(TLV_IE, tag=0x84):
|
||||||
# Bearer descriptionTLV DO as per TS 31.111
|
# Bearer descriptionTLV DO as per TS 31.111
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class EF_NCP_IP_Collection(TLV_IE_Collection,
|
class EF_NCP_IP_Collection(TLV_IE_Collection,
|
||||||
nested=[AccessPointName, Login, Password, BearerDescription]):
|
nested=[AccessPointName, Login, Password, BearerDescription]):
|
||||||
pass
|
pass
|
||||||
@@ -809,26 +893,36 @@ class EF_NCP_IP(LinFixedEF):
|
|||||||
self._tlv = EF_NCP_IP.EF_NCP_IP_Collection
|
self._tlv = EF_NCP_IP.EF_NCP_IP_Collection
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.91
|
# TS 31.102 Section 4.2.91
|
||||||
|
|
||||||
|
|
||||||
class EF_EPSLOCI(TransparentEF):
|
class EF_EPSLOCI(TransparentEF):
|
||||||
def __init__(self, fid='6fe3', sfid=0x1e, name='EF.EPSLOCI', size={18, 18},
|
def __init__(self, fid='6fe3', sfid=0x1e, name='EF.EPSLOCI', size={18, 18},
|
||||||
desc='EPS Location Information'):
|
desc='EPS Location Information'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
upd_status_constr = Enum(Byte, updated=0, not_updated=1, roaming_not_allowed=2)
|
upd_status_constr = Enum(
|
||||||
|
Byte, updated=0, not_updated=1, roaming_not_allowed=2)
|
||||||
self._construct = Struct('guti'/Bytes(12), 'last_visited_registered_tai'/Bytes(5),
|
self._construct = Struct('guti'/Bytes(12), 'last_visited_registered_tai'/Bytes(5),
|
||||||
'eps_update_status'/upd_status_constr)
|
'eps_update_status'/upd_status_constr)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.92
|
# TS 31.102 Section 4.2.92
|
||||||
|
|
||||||
|
|
||||||
class EF_EPSNSC(LinFixedEF):
|
class EF_EPSNSC(LinFixedEF):
|
||||||
class KSI_ASME(BER_TLV_IE, tag=0x80):
|
class KSI_ASME(BER_TLV_IE, tag=0x80):
|
||||||
_construct = Int8ub
|
_construct = Int8ub
|
||||||
|
|
||||||
class K_ASME(BER_TLV_IE, tag=0x81):
|
class K_ASME(BER_TLV_IE, tag=0x81):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class UplinkNASCount(BER_TLV_IE, tag=0x82):
|
class UplinkNASCount(BER_TLV_IE, tag=0x82):
|
||||||
_construct = Int32ub
|
_construct = Int32ub
|
||||||
|
|
||||||
class DownlinkNASCount(BER_TLV_IE, tag=0x83):
|
class DownlinkNASCount(BER_TLV_IE, tag=0x83):
|
||||||
_construct = Int32ub
|
_construct = Int32ub
|
||||||
|
|
||||||
class IDofNASAlgorithms(BER_TLV_IE, tag=0x84):
|
class IDofNASAlgorithms(BER_TLV_IE, tag=0x84):
|
||||||
_construct = HexAdapter(GreedyBytes)
|
_construct = HexAdapter(GreedyBytes)
|
||||||
|
|
||||||
class EPS_NAS_Security_Context(BER_TLV_IE, tag=0xa0,
|
class EPS_NAS_Security_Context(BER_TLV_IE, tag=0xa0,
|
||||||
nested=[KSI_ASME, K_ASME, UplinkNASCount, DownlinkNASCount,
|
nested=[KSI_ASME, K_ASME, UplinkNASCount, DownlinkNASCount,
|
||||||
IDofNASAlgorithms]):
|
IDofNASAlgorithms]):
|
||||||
@@ -839,13 +933,18 @@ class EF_EPSNSC(LinFixedEF):
|
|||||||
self._tlv = EF_EPSNSC.EPS_NAS_Security_Context
|
self._tlv = EF_EPSNSC.EPS_NAS_Security_Context
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.96
|
# TS 31.102 Section 4.2.96
|
||||||
|
|
||||||
|
|
||||||
class EF_PWS(TransparentEF):
|
class EF_PWS(TransparentEF):
|
||||||
def __init__(self, fid='6fec', sfid=None, name='EF.PWS', desc='Public Warning System', size={1, 1}):
|
def __init__(self, fid='6fec', sfid=None, name='EF.PWS', desc='Public Warning System', size={1, 1}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
pws_config = FlagsEnum(Byte, ignore_pws_in_hplmn_and_equivalent=1, ignore_pws_in_vplmn=2)
|
pws_config = FlagsEnum(
|
||||||
|
Byte, ignore_pws_in_hplmn_and_equivalent=1, ignore_pws_in_vplmn=2)
|
||||||
self._construct = Struct('pws_configuration'/pws_config)
|
self._construct = Struct('pws_configuration'/pws_config)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.101
|
# TS 31.102 Section 4.2.101
|
||||||
|
|
||||||
|
|
||||||
class EF_IPS(CyclicEF):
|
class EF_IPS(CyclicEF):
|
||||||
def __init__(self, fid='6ff1', sfid=None, name='EF.IPS', rec_len={4, 4},
|
def __init__(self, fid='6ff1', sfid=None, name='EF.IPS', rec_len={4, 4},
|
||||||
desc='IMEI(SV) Pairing Status'):
|
desc='IMEI(SV) Pairing Status'):
|
||||||
@@ -854,6 +953,8 @@ class EF_IPS(CyclicEF):
|
|||||||
'link_to_ef_ipd'/Int8ub, 'rfu'/Byte)
|
'link_to_ef_ipd'/Int8ub, 'rfu'/Byte)
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.103
|
# TS 31.102 Section 4.2.103
|
||||||
|
|
||||||
|
|
||||||
class EF_ePDGId(TransparentEF):
|
class EF_ePDGId(TransparentEF):
|
||||||
class ePDGId(BER_TLV_IE, tag=0x80, nested=[]):
|
class ePDGId(BER_TLV_IE, tag=0x80, nested=[]):
|
||||||
_construct = Struct('type_of_ePDG_address'/Enum(Byte, FQDN=0, IPv4=1, IPv6=2),
|
_construct = Struct('type_of_ePDG_address'/Enum(Byte, FQDN=0, IPv4=1, IPv6=2),
|
||||||
@@ -861,11 +962,14 @@ class EF_ePDGId(TransparentEF):
|
|||||||
{'FQDN': GreedyString("utf8"),
|
{'FQDN': GreedyString("utf8"),
|
||||||
'IPv4': HexAdapter(GreedyBytes),
|
'IPv4': HexAdapter(GreedyBytes),
|
||||||
'IPv6': HexAdapter(GreedyBytes)}))
|
'IPv6': HexAdapter(GreedyBytes)}))
|
||||||
|
|
||||||
def __init__(self, fid='6ff3', sfid=None, name='EF.eDPDGId', desc='Home ePDG Identifier'):
|
def __init__(self, fid='6ff3', sfid=None, name='EF.eDPDGId', desc='Home ePDG Identifier'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_ePDGId.ePDGId
|
self._tlv = EF_ePDGId.ePDGId
|
||||||
|
|
||||||
# TS 31.102 Section 4.2.106
|
# TS 31.102 Section 4.2.106
|
||||||
|
|
||||||
|
|
||||||
class EF_FromPreferred(TransparentEF):
|
class EF_FromPreferred(TransparentEF):
|
||||||
def __init__(self, fid='6ff7', sfid=None, name='EF.FromPreferred', size={1, 1},
|
def __init__(self, fid='6ff7', sfid=None, name='EF.FromPreferred', size={1, 1},
|
||||||
desc='From Preferred'):
|
desc='From Preferred'):
|
||||||
@@ -877,15 +981,20 @@ class EF_FromPreferred(TransparentEF):
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.11.2
|
# TS 31.102 Section 4.4.11.2
|
||||||
|
|
||||||
|
|
||||||
class EF_5GS3GPPLOCI(TransparentEF):
|
class EF_5GS3GPPLOCI(TransparentEF):
|
||||||
def __init__(self, fid='4f01', sfid=0x01, name='EF.5GS3GPPLOCI', size={20, 20},
|
def __init__(self, fid='4f01', sfid=0x01, name='EF.5GS3GPPLOCI', size={20, 20},
|
||||||
desc='5S 3GP location information'):
|
desc='5S 3GP location information'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
upd_status_constr = Enum(Byte, updated=0, not_updated=1, roaming_not_allowed=2)
|
upd_status_constr = Enum(
|
||||||
|
Byte, updated=0, not_updated=1, roaming_not_allowed=2)
|
||||||
self._construct = Struct('5g_guti'/Bytes(13), 'last_visited_registered_tai_in_5gs'/Bytes(6),
|
self._construct = Struct('5g_guti'/Bytes(13), 'last_visited_registered_tai_in_5gs'/Bytes(6),
|
||||||
'5gs_update_status'/upd_status_constr)
|
'5gs_update_status'/upd_status_constr)
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.11.7
|
# TS 31.102 Section 4.4.11.7
|
||||||
|
|
||||||
|
|
||||||
class EF_UAC_AIC(TransparentEF):
|
class EF_UAC_AIC(TransparentEF):
|
||||||
def __init__(self, fid='4f06', sfid=0x06, name='EF.UAC_AIC', size={4, 4},
|
def __init__(self, fid='4f06', sfid=0x06, name='EF.UAC_AIC', size={4, 4},
|
||||||
desc='UAC Access Identities Configuration'):
|
desc='UAC Access Identities Configuration'):
|
||||||
@@ -895,22 +1004,30 @@ class EF_UAC_AIC(TransparentEF):
|
|||||||
self._construct = Struct('uac_access_id_config'/cfg_constr)
|
self._construct = Struct('uac_access_id_config'/cfg_constr)
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.11.9
|
# TS 31.102 Section 4.4.11.9
|
||||||
|
|
||||||
|
|
||||||
class EF_OPL5G(LinFixedEF):
|
class EF_OPL5G(LinFixedEF):
|
||||||
def __init__(self, fid='6f08', sfid=0x08, name='EF.OPL5G', desc='5GS Operator PLMN List'):
|
def __init__(self, fid='6f08', sfid=0x08, name='EF.OPL5G', desc='5GS Operator PLMN List'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={10,None})
|
super().__init__(fid=fid, sfid=sfid,
|
||||||
|
name=name, desc=desc, rec_len={10, None})
|
||||||
self._construct = Struct('tai'/Bytes(9), 'pnn_record_id'/Int8ub)
|
self._construct = Struct('tai'/Bytes(9), 'pnn_record_id'/Int8ub)
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.11.10
|
# TS 31.102 Section 4.4.11.10
|
||||||
|
|
||||||
|
|
||||||
class EF_SUPI_NAI(TransparentEF):
|
class EF_SUPI_NAI(TransparentEF):
|
||||||
class NetworkSpecificIdentifier(TLV_IE, tag=0x80):
|
class NetworkSpecificIdentifier(TLV_IE, tag=0x80):
|
||||||
# RFC 7542 encoded as UTF-8 string
|
# RFC 7542 encoded as UTF-8 string
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
|
|
||||||
class GlobalLineIdentifier(TLV_IE, tag=0x81):
|
class GlobalLineIdentifier(TLV_IE, tag=0x81):
|
||||||
# TS 23.003 clause 28.16.2
|
# TS 23.003 clause 28.16.2
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class GlobalCableIdentifier(TLV_IE, tag=0x82):
|
class GlobalCableIdentifier(TLV_IE, tag=0x82):
|
||||||
# TS 23.003 clause 28.15.2
|
# TS 23.003 clause 28.15.2
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class NAI_TLV_Collection(TLV_IE_Collection,
|
class NAI_TLV_Collection(TLV_IE_Collection,
|
||||||
nested=[NetworkSpecificIdentifier, GlobalLineIdentifier, GlobalCableIdentifier]):
|
nested=[NetworkSpecificIdentifier, GlobalLineIdentifier, GlobalCableIdentifier]):
|
||||||
pass
|
pass
|
||||||
@@ -919,6 +1036,7 @@ class EF_SUPI_NAI(TransparentEF):
|
|||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_SUPI_NAI.NAI_TLV_Collection
|
self._tlv = EF_SUPI_NAI.NAI_TLV_Collection
|
||||||
|
|
||||||
|
|
||||||
class EF_TN3GPPSNN(TransparentEF):
|
class EF_TN3GPPSNN(TransparentEF):
|
||||||
class ServingNetworkName(BER_TLV_IE, tag=0x80):
|
class ServingNetworkName(BER_TLV_IE, tag=0x80):
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
@@ -928,25 +1046,39 @@ class EF_TN3GPPSNN(TransparentEF):
|
|||||||
self._tlv = EF_TN3GPPSNN.ServingNetworkName
|
self._tlv = EF_TN3GPPSNN.ServingNetworkName
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.5
|
# TS 31.102 Section 4.4.5
|
||||||
|
|
||||||
|
|
||||||
class DF_WLAN(CardDF):
|
class DF_WLAN(CardDF):
|
||||||
def __init__(self, fid='5f40', name='DF.WLAN', desc='Files for WLAN purpose'):
|
def __init__(self, fid='5f40', name='DF.WLAN', desc='Files for WLAN purpose'):
|
||||||
super().__init__(fid=fid, name=name, desc=desc)
|
super().__init__(fid=fid, name=name, desc=desc)
|
||||||
files = [
|
files = [
|
||||||
TransparentEF('4f41', 0x01, 'EF.Pseudo', 'Pseudonym'),
|
TransparentEF('4f41', 0x01, 'EF.Pseudo', 'Pseudonym'),
|
||||||
TransparentEF('4f42', 0x02, 'EF.UPLMNWLAN', 'User controlled PLMN selector for I-WLAN Access'),
|
TransparentEF('4f42', 0x02, 'EF.UPLMNWLAN',
|
||||||
TransparentEF('4f43', 0x03, 'EF.OPLMNWLAN', 'Operator controlled PLMN selector for I-WLAN Access'),
|
'User controlled PLMN selector for I-WLAN Access'),
|
||||||
LinFixedEF('4f44', 0x04, 'EF.UWSIDL', 'User controlled WLAN Specific Identifier List'),
|
TransparentEF('4f43', 0x03, 'EF.OPLMNWLAN',
|
||||||
LinFixedEF('4f45', 0x05, 'EF.OWSIDL', 'Operator controlled WLAN Specific Identifier List'),
|
'Operator controlled PLMN selector for I-WLAN Access'),
|
||||||
TransparentEF('4f46', 0x06, 'EF.WRI', 'WLAN Reauthentication Identity'),
|
LinFixedEF('4f44', 0x04, 'EF.UWSIDL',
|
||||||
LinFixedEF('4f47', 0x07, 'EF.HWSIDL', 'Home I-WLAN Specific Identifier List'),
|
'User controlled WLAN Specific Identifier List'),
|
||||||
TransparentEF('4f48', 0x08, 'EF.WEHPLMNPI', 'I-WLAN Equivalent HPLMN Presentation Indication'),
|
LinFixedEF('4f45', 0x05, 'EF.OWSIDL',
|
||||||
TransparentEF('4f49', 0x09, 'EF.WHPI', 'I-WLAN HPLMN Priority Indication'),
|
'Operator controlled WLAN Specific Identifier List'),
|
||||||
TransparentEF('4f4a', 0x0a, 'EF.WLRPLMN', 'I-WLAN Last Registered PLMN'),
|
TransparentEF('4f46', 0x06, 'EF.WRI',
|
||||||
TransparentEF('4f4b', 0x0b, 'EF.HPLMNDAI', 'HPLMN Direct Access Indicator'),
|
'WLAN Reauthentication Identity'),
|
||||||
|
LinFixedEF('4f47', 0x07, 'EF.HWSIDL',
|
||||||
|
'Home I-WLAN Specific Identifier List'),
|
||||||
|
TransparentEF('4f48', 0x08, 'EF.WEHPLMNPI',
|
||||||
|
'I-WLAN Equivalent HPLMN Presentation Indication'),
|
||||||
|
TransparentEF('4f49', 0x09, 'EF.WHPI',
|
||||||
|
'I-WLAN HPLMN Priority Indication'),
|
||||||
|
TransparentEF('4f4a', 0x0a, 'EF.WLRPLMN',
|
||||||
|
'I-WLAN Last Registered PLMN'),
|
||||||
|
TransparentEF('4f4b', 0x0b, 'EF.HPLMNDAI',
|
||||||
|
'HPLMN Direct Access Indicator'),
|
||||||
]
|
]
|
||||||
self.add_files(files)
|
self.add_files(files)
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.6
|
# TS 31.102 Section 4.4.6
|
||||||
|
|
||||||
|
|
||||||
class DF_HNB(CardDF):
|
class DF_HNB(CardDF):
|
||||||
def __init__(self, fid='5f50', name='DF.HNB', desc='Files for HomeNodeB purpose'):
|
def __init__(self, fid='5f50', name='DF.HNB', desc='Files for HomeNodeB purpose'):
|
||||||
super().__init__(fid=fid, name=name, desc=desc)
|
super().__init__(fid=fid, name=name, desc=desc)
|
||||||
@@ -961,47 +1093,65 @@ class DF_HNB(CardDF):
|
|||||||
self.add_files(files)
|
self.add_files(files)
|
||||||
|
|
||||||
# TS 31.102 Section 4.4.8
|
# TS 31.102 Section 4.4.8
|
||||||
|
|
||||||
|
|
||||||
class DF_ProSe(CardDF):
|
class DF_ProSe(CardDF):
|
||||||
def __init__(self, fid='5f90', name='DF.ProSe', desc='Files for ProSe purpose'):
|
def __init__(self, fid='5f90', name='DF.ProSe', desc='Files for ProSe purpose'):
|
||||||
super().__init__(fid=fid, name=name, desc=desc)
|
super().__init__(fid=fid, name=name, desc=desc)
|
||||||
files = [
|
files = [
|
||||||
LinFixedEF('4f01', 0x01, 'EF.PROSE_MON', 'ProSe Monitoring Parameters'),
|
LinFixedEF('4f01', 0x01, 'EF.PROSE_MON',
|
||||||
LinFixedEF('4f02', 0x02, 'EF.PROSE_ANN', 'ProSe Announcing Parameters'),
|
'ProSe Monitoring Parameters'),
|
||||||
|
LinFixedEF('4f02', 0x02, 'EF.PROSE_ANN',
|
||||||
|
'ProSe Announcing Parameters'),
|
||||||
LinFixedEF('4f03', 0x03, 'EF.PROSEFUNC', 'HPLMN ProSe Function'),
|
LinFixedEF('4f03', 0x03, 'EF.PROSEFUNC', 'HPLMN ProSe Function'),
|
||||||
TransparentEF('4f04', 0x04, 'EF.PROSE_RADIO_COM', 'ProSe Direct Communication Radio Parameters'),
|
TransparentEF('4f04', 0x04, 'EF.PROSE_RADIO_COM',
|
||||||
TransparentEF('4f05', 0x05, 'EF.PROSE_RADIO_MON', 'ProSe Direct Discovery Monitoring Radio Parameters'),
|
'ProSe Direct Communication Radio Parameters'),
|
||||||
TransparentEF('4f06', 0x06, 'EF.PROSE_RADIO_ANN', 'ProSe Direct Discovery Announcing Radio Parameters'),
|
TransparentEF('4f05', 0x05, 'EF.PROSE_RADIO_MON',
|
||||||
LinFixedEF('4f07', 0x07, 'EF.PROSE_POLICY', 'ProSe Policy Parameters'),
|
'ProSe Direct Discovery Monitoring Radio Parameters'),
|
||||||
|
TransparentEF('4f06', 0x06, 'EF.PROSE_RADIO_ANN',
|
||||||
|
'ProSe Direct Discovery Announcing Radio Parameters'),
|
||||||
|
LinFixedEF('4f07', 0x07, 'EF.PROSE_POLICY',
|
||||||
|
'ProSe Policy Parameters'),
|
||||||
LinFixedEF('4f08', 0x08, 'EF.PROSE_PLMN', 'ProSe PLMN Parameters'),
|
LinFixedEF('4f08', 0x08, 'EF.PROSE_PLMN', 'ProSe PLMN Parameters'),
|
||||||
TransparentEF('4f09', 0x09, 'EF.PROSE_GC', 'ProSe Group Counter'),
|
TransparentEF('4f09', 0x09, 'EF.PROSE_GC', 'ProSe Group Counter'),
|
||||||
TransparentEF('4f10', 0x10, 'EF.PST', 'ProSe Service Table'),
|
TransparentEF('4f10', 0x10, 'EF.PST', 'ProSe Service Table'),
|
||||||
TransparentEF('4f11', 0x11, 'EF.UIRC', 'ProSe UsageInformationReportingConfiguration'),
|
TransparentEF('4f11', 0x11, 'EF.UIRC',
|
||||||
LinFixedEF('4f12', 0x12, 'EF.PROSE_GM_DISCOVERY', 'ProSe Group Member Discovery Parameters'),
|
'ProSe UsageInformationReportingConfiguration'),
|
||||||
LinFixedEF('4f13', 0x13, 'EF.PROSE_RELAY', 'ProSe Relay Parameters'),
|
LinFixedEF('4f12', 0x12, 'EF.PROSE_GM_DISCOVERY',
|
||||||
TransparentEF('4f14', 0x14, 'EF.PROSE_RELAY_DISCOVERY', 'ProSe Relay Discovery Parameters'),
|
'ProSe Group Member Discovery Parameters'),
|
||||||
|
LinFixedEF('4f13', 0x13, 'EF.PROSE_RELAY',
|
||||||
|
'ProSe Relay Parameters'),
|
||||||
|
TransparentEF('4f14', 0x14, 'EF.PROSE_RELAY_DISCOVERY',
|
||||||
|
'ProSe Relay Discovery Parameters'),
|
||||||
]
|
]
|
||||||
self.add_files(files)
|
self.add_files(files)
|
||||||
|
|
||||||
|
|
||||||
class DF_USIM_5GS(CardDF):
|
class DF_USIM_5GS(CardDF):
|
||||||
def __init__(self, fid='5FC0', name='DF.5GS', desc='5GS related files'):
|
def __init__(self, fid='5FC0', name='DF.5GS', desc='5GS related files'):
|
||||||
super().__init__(fid=fid, name=name, desc=desc)
|
super().__init__(fid=fid, name=name, desc=desc)
|
||||||
files = [
|
files = [
|
||||||
# I'm looking at 31.102 R16.6
|
# I'm looking at 31.102 R16.6
|
||||||
EF_5GS3GPPLOCI(),
|
EF_5GS3GPPLOCI(),
|
||||||
EF_5GS3GPPLOCI('4f02', 0x02, 'EF.5GSN3GPPLOCI', '5GS non-3GPP location information'),
|
EF_5GS3GPPLOCI('4f02', 0x02, 'EF.5GSN3GPPLOCI',
|
||||||
|
'5GS non-3GPP location information'),
|
||||||
EF_5GS3GPPNSC(),
|
EF_5GS3GPPNSC(),
|
||||||
EF_5GS3GPPNSC('4f04', 0x04, 'EF.5GSN3GPPNSC', '5GS non-3GPP Access NAS Security Context'),
|
EF_5GS3GPPNSC('4f04', 0x04, 'EF.5GSN3GPPNSC',
|
||||||
|
'5GS non-3GPP Access NAS Security Context'),
|
||||||
EF_5GAUTHKEYS(),
|
EF_5GAUTHKEYS(),
|
||||||
EF_UAC_AIC(),
|
EF_UAC_AIC(),
|
||||||
EF_SUCI_Calc_Info(),
|
EF_SUCI_Calc_Info(),
|
||||||
EF_OPL5G(),
|
EF_OPL5G(),
|
||||||
EF_SUPI_NAI(),
|
EF_SUPI_NAI(),
|
||||||
TransparentEF('4F0A', 0x0a, 'EF.Routing_Indicator', 'Routing Indicator', size={4,4}),
|
TransparentEF('4F0A', 0x0a, 'EF.Routing_Indicator',
|
||||||
TransparentEF('4F0B', 0x0b, 'EF.URSP', 'UE Route Selector Policies per PLMN'),
|
'Routing Indicator', size={4, 4}),
|
||||||
|
TransparentEF('4F0B', 0x0b, 'EF.URSP',
|
||||||
|
'UE Route Selector Policies per PLMN'),
|
||||||
EF_TN3GPPSNN(),
|
EF_TN3GPPSNN(),
|
||||||
]
|
]
|
||||||
self.add_files(files)
|
self.add_files(files)
|
||||||
|
|
||||||
|
|
||||||
class ADF_USIM(CardADF):
|
class ADF_USIM(CardADF):
|
||||||
def __init__(self, aid='a0000000871002', name='ADF.USIM', fid=None, sfid=None,
|
def __init__(self, aid='a0000000871002', name='ADF.USIM', fid=None, sfid=None,
|
||||||
desc='USIM Application'):
|
desc='USIM Application'):
|
||||||
@@ -1013,20 +1163,25 @@ class ADF_USIM(CardADF):
|
|||||||
EF_LI(sfid=0x02),
|
EF_LI(sfid=0x02),
|
||||||
EF_IMSI(sfid=0x07),
|
EF_IMSI(sfid=0x07),
|
||||||
EF_Keys(),
|
EF_Keys(),
|
||||||
EF_Keys('6f09', 0x09, 'EF.KeysPS', desc='Ciphering and Integrity Keys for PS domain'),
|
EF_Keys('6f09', 0x09, 'EF.KeysPS',
|
||||||
|
desc='Ciphering and Integrity Keys for PS domain'),
|
||||||
EF_xPLMNwAcT('6f60', 0x0a, 'EF.PLMNwAcT',
|
EF_xPLMNwAcT('6f60', 0x0a, 'EF.PLMNwAcT',
|
||||||
'User controlled PLMN Selector with Access Technology'),
|
'User controlled PLMN Selector with Access Technology'),
|
||||||
EF_HPPLMN(),
|
EF_HPPLMN(),
|
||||||
EF_ACMmax(),
|
EF_ACMmax(),
|
||||||
EF_UServiceTable('6f38', 0x04, 'EF.UST', 'USIM Service Table', size={1,17}, table=EF_UST_map),
|
EF_UServiceTable('6f38', 0x04, 'EF.UST', 'USIM Service Table', size={
|
||||||
CyclicEF('6f39', None, 'EF.ACM', 'Accumulated call meter', rec_len={3,3}),
|
1, 17}, table=EF_UST_map),
|
||||||
|
CyclicEF('6f39', None, 'EF.ACM',
|
||||||
|
'Accumulated call meter', rec_len={3, 3}),
|
||||||
TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),
|
TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),
|
||||||
TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),
|
TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),
|
||||||
EF_SPN(),
|
EF_SPN(),
|
||||||
TransparentEF('6f41', None, 'EF.PUCT', 'Price per unit and currency table', size={5,5}),
|
TransparentEF('6f41', None, 'EF.PUCT',
|
||||||
|
'Price per unit and currency table', size={5, 5}),
|
||||||
EF_CBMI(),
|
EF_CBMI(),
|
||||||
EF_ACC(sfid=0x06),
|
EF_ACC(sfid=0x06),
|
||||||
EF_PLMNsel('6f7b', 0x0d, 'EF.FPLMN', 'Forbidden PLMNs', size={12,None}),
|
EF_PLMNsel('6f7b', 0x0d, 'EF.FPLMN',
|
||||||
|
'Forbidden PLMNs', size={12, None}),
|
||||||
EF_LOCI(),
|
EF_LOCI(),
|
||||||
EF_AD(),
|
EF_AD(),
|
||||||
EF_CBMID(sfid=0x0e),
|
EF_CBMID(sfid=0x0e),
|
||||||
@@ -1054,7 +1209,8 @@ class ADF_USIM(CardADF):
|
|||||||
EF_ADN('6f4d', None, 'EF.BDN', 'Barred Dialling Numbers'),
|
EF_ADN('6f4d', None, 'EF.BDN', 'Barred Dialling Numbers'),
|
||||||
EF_EXT('6f55', None, 'EF.EXT4', 'Extension4 (BDN/SSC)'),
|
EF_EXT('6f55', None, 'EF.EXT4', 'Extension4 (BDN/SSC)'),
|
||||||
EF_CMI(),
|
EF_CMI(),
|
||||||
EF_UServiceTable('6f56', 0x05, 'EF.EST', 'Enabled Services Table', size={1,None}, table=EF_EST_map),
|
EF_UServiceTable('6f56', 0x05, 'EF.EST', 'Enabled Services Table', size={
|
||||||
|
1, None}, table=EF_EST_map),
|
||||||
EF_ACL(),
|
EF_ACL(),
|
||||||
EF_DCK(),
|
EF_DCK(),
|
||||||
EF_CNL(),
|
EF_CNL(),
|
||||||
@@ -1069,9 +1225,11 @@ class ADF_USIM(CardADF):
|
|||||||
EF_ADN('6fc7', None, 'EF.MBDN', 'Mailbox Dialling Numbers'),
|
EF_ADN('6fc7', None, 'EF.MBDN', 'Mailbox Dialling Numbers'),
|
||||||
EF_MBI(),
|
EF_MBI(),
|
||||||
EF_MWIS(),
|
EF_MWIS(),
|
||||||
EF_ADN('6fcb', None, 'EF.CFIS', 'Call Forwarding Indication Status'),
|
EF_ADN('6fcb', None, 'EF.CFIS',
|
||||||
|
'Call Forwarding Indication Status'),
|
||||||
EF_EXT('6fcc', None, 'EF.EXT7', 'Extension7 (CFIS)'),
|
EF_EXT('6fcc', None, 'EF.EXT7', 'Extension7 (CFIS)'),
|
||||||
TransparentEF('6fcd', None, 'EF.SPDI', 'Service Provider Display Information'),
|
TransparentEF('6fcd', None, 'EF.SPDI',
|
||||||
|
'Service Provider Display Information'),
|
||||||
EF_MMSN(),
|
EF_MMSN(),
|
||||||
EF_EXT('6fcf', None, 'EF.EXT8', 'Extension8 (MMSN)'),
|
EF_EXT('6fcf', None, 'EF.EXT8', 'Extension8 (MMSN)'),
|
||||||
EF_MMSICP(),
|
EF_MMSICP(),
|
||||||
@@ -1081,28 +1239,37 @@ class ADF_USIM(CardADF):
|
|||||||
EF_VGCS(),
|
EF_VGCS(),
|
||||||
EF_VGCSS(),
|
EF_VGCSS(),
|
||||||
EF_VGCS('6fb3', None, 'EF.VBS', 'Voice Broadcast Service'),
|
EF_VGCS('6fb3', None, 'EF.VBS', 'Voice Broadcast Service'),
|
||||||
EF_VGCSS('6fb4', None, 'EF.VBSS', 'Voice Broadcast Service Status'),
|
EF_VGCSS('6fb4', None, 'EF.VBSS',
|
||||||
|
'Voice Broadcast Service Status'),
|
||||||
EF_VGCSCA(),
|
EF_VGCSCA(),
|
||||||
EF_VGCSCA('6fd5', None, 'EF.VBCSCA', 'Voice Broadcast Service Ciphering Algorithm'),
|
EF_VGCSCA('6fd5', None, 'EF.VBCSCA',
|
||||||
|
'Voice Broadcast Service Ciphering Algorithm'),
|
||||||
EF_GBABP(),
|
EF_GBABP(),
|
||||||
EF_MSK(),
|
EF_MSK(),
|
||||||
EF_MUK(),
|
EF_MUK(),
|
||||||
EF_GBANL(),
|
EF_GBANL(),
|
||||||
EF_PLMNsel('6fd9', 0x1d, 'EF.EHPLMN', 'Equivalent HPLMN', size={12,None}),
|
EF_PLMNsel('6fd9', 0x1d, 'EF.EHPLMN',
|
||||||
|
'Equivalent HPLMN', size={12, None}),
|
||||||
EF_EHPLMNPI(),
|
EF_EHPLMNPI(),
|
||||||
EF_NAFKCA(),
|
EF_NAFKCA(),
|
||||||
TransparentEF('6fde', None, 'EF.SPNI', 'Service Provider Name Icon'),
|
TransparentEF('6fde', None, 'EF.SPNI',
|
||||||
|
'Service Provider Name Icon'),
|
||||||
LinFixedEF('6fdf', None, 'EF.PNNI', 'PLMN Network Name Icon'),
|
LinFixedEF('6fdf', None, 'EF.PNNI', 'PLMN Network Name Icon'),
|
||||||
EF_NCP_IP(),
|
EF_NCP_IP(),
|
||||||
EF_EPSLOCI('6fe3', 0x1e, 'EF.EPSLOCI', 'EPS location information'),
|
EF_EPSLOCI('6fe3', 0x1e, 'EF.EPSLOCI', 'EPS location information'),
|
||||||
EF_EPSNSC(),
|
EF_EPSNSC(),
|
||||||
TransparentEF('6fe6', None, 'EF.UFC', 'USAT Facility Control', size={1,16}),
|
TransparentEF('6fe6', None, 'EF.UFC',
|
||||||
TransparentEF('6fe8', None, 'EF.NASCONFIG', 'Non Access Stratum Configuration'),
|
'USAT Facility Control', size={1, 16}),
|
||||||
|
TransparentEF('6fe8', None, 'EF.NASCONFIG',
|
||||||
|
'Non Access Stratum Configuration'),
|
||||||
# UICC IARI (only in cards that have no ISIM)
|
# UICC IARI (only in cards that have no ISIM)
|
||||||
EF_PWS(),
|
EF_PWS(),
|
||||||
LinFixedEF('6fed', None, 'EF.FDNURI', 'Fixed Dialling Numbers URI'),
|
LinFixedEF('6fed', None, 'EF.FDNURI',
|
||||||
LinFixedEF('6fee', None, 'EF.BDNURI', 'Barred Dialling Numbers URI'),
|
'Fixed Dialling Numbers URI'),
|
||||||
LinFixedEF('6fef', None, 'EF.SDNURI', 'Service Dialling Numbers URI'),
|
LinFixedEF('6fee', None, 'EF.BDNURI',
|
||||||
|
'Barred Dialling Numbers URI'),
|
||||||
|
LinFixedEF('6fef', None, 'EF.SDNURI',
|
||||||
|
'Service Dialling Numbers URI'),
|
||||||
EF_IPS(),
|
EF_IPS(),
|
||||||
EF_ePDGId(),
|
EF_ePDGId(),
|
||||||
# FIXME: from EF_ePDGSelection onwards
|
# FIXME: from EF_ePDGSelection onwards
|
||||||
@@ -1131,6 +1298,7 @@ class ADF_USIM(CardADF):
|
|||||||
authenticate_parser.add_argument('rand', help='Random challenge')
|
authenticate_parser.add_argument('rand', help='Random challenge')
|
||||||
authenticate_parser.add_argument('autn', help='Authentication Nonce')
|
authenticate_parser.add_argument('autn', help='Authentication Nonce')
|
||||||
#authenticate_parser.add_argument('--context', help='Authentication context', default='3G')
|
#authenticate_parser.add_argument('--context', help='Authentication context', default='3G')
|
||||||
|
|
||||||
@cmd2.with_argparser(authenticate_parser)
|
@cmd2.with_argparser(authenticate_parser)
|
||||||
def do_authenticate(self, opts):
|
def do_authenticate(self, opts):
|
||||||
"""Perform Authentication and Key Agreement (AKA)."""
|
"""Perform Authentication and Key Agreement (AKA)."""
|
||||||
@@ -1151,7 +1319,8 @@ class ADF_USIM(CardADF):
|
|||||||
"""Send an ENVELOPE command to the card."""
|
"""Send an ENVELOPE command to the card."""
|
||||||
tpdu_ie = SMS_TPDU()
|
tpdu_ie = SMS_TPDU()
|
||||||
tpdu_ie.from_bytes(h2b(arg))
|
tpdu_ie.from_bytes(h2b(arg))
|
||||||
dev_ids = DeviceIdentities(decoded={'source_dev_id':'network','dest_dev_id':'uicc'})
|
dev_ids = DeviceIdentities(
|
||||||
|
decoded={'source_dev_id': 'network', 'dest_dev_id': 'uicc'})
|
||||||
sms_dl = SMSPPDownload(children=[dev_ids, tpdu_ie])
|
sms_dl = SMSPPDownload(children=[dev_ids, tpdu_ie])
|
||||||
(data, sw) = self._cmd.card._scc.envelope(b2h(sms_dl.to_tlv()))
|
(data, sw) = self._cmd.card._scc.envelope(b2h(sms_dl.to_tlv()))
|
||||||
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
|
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
|
||||||
@@ -1168,6 +1337,7 @@ sw_usim = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CardApplicationUSIM(CardApplication):
|
class CardApplicationUSIM(CardApplication):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('USIM', adf=ADF_USIM(), sw=sw_usim)
|
super().__init__('USIM', adf=ADF_USIM(), sw=sw_usim)
|
||||||
|
|||||||
@@ -78,83 +78,114 @@ EF_ISIM_ADF_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.2
|
# TS 31.103 Section 4.2.2
|
||||||
|
|
||||||
|
|
||||||
class EF_IMPI(TransparentEF):
|
class EF_IMPI(TransparentEF):
|
||||||
class nai(BER_TLV_IE, tag=0x80):
|
class nai(BER_TLV_IE, tag=0x80):
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
|
|
||||||
def __init__(self, fid='6f02', sfid=0x02, name='EF.IMPI', desc='IMS private user identity'):
|
def __init__(self, fid='6f02', sfid=0x02, name='EF.IMPI', desc='IMS private user identity'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_IMPI.nai
|
self._tlv = EF_IMPI.nai
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.3
|
# TS 31.103 Section 4.2.3
|
||||||
|
|
||||||
|
|
||||||
class EF_DOMAIN(TransparentEF):
|
class EF_DOMAIN(TransparentEF):
|
||||||
class domain(BER_TLV_IE, tag=0x80):
|
class domain(BER_TLV_IE, tag=0x80):
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
|
|
||||||
def __init__(self, fid='6f05', sfid=0x05, name='EF.DOMAIN', desc='Home Network Domain Name'):
|
def __init__(self, fid='6f05', sfid=0x05, name='EF.DOMAIN', desc='Home Network Domain Name'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_DOMAIN.domain
|
self._tlv = EF_DOMAIN.domain
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.4
|
# TS 31.103 Section 4.2.4
|
||||||
|
|
||||||
|
|
||||||
class EF_IMPU(LinFixedEF):
|
class EF_IMPU(LinFixedEF):
|
||||||
class impu(BER_TLV_IE, tag=0x80):
|
class impu(BER_TLV_IE, tag=0x80):
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
|
|
||||||
def __init__(self, fid='6f04', sfid=0x04, name='EF.IMPU', desc='IMS public user identity'):
|
def __init__(self, fid='6f04', sfid=0x04, name='EF.IMPU', desc='IMS public user identity'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_IMPU.impu
|
self._tlv = EF_IMPU.impu
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.8
|
# TS 31.103 Section 4.2.8
|
||||||
|
|
||||||
|
|
||||||
class EF_PCSCF(LinFixedEF):
|
class EF_PCSCF(LinFixedEF):
|
||||||
def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address'):
|
def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
|
|
||||||
def _decode_record_hex(self, raw_hex):
|
def _decode_record_hex(self, raw_hex):
|
||||||
addr, addr_type = dec_addr_tlv(raw_hex)
|
addr, addr_type = dec_addr_tlv(raw_hex)
|
||||||
return {"addr": addr, "addr_type": addr_type}
|
return {"addr": addr, "addr_type": addr_type}
|
||||||
|
|
||||||
def _encode_record_hex(self, json_in):
|
def _encode_record_hex(self, json_in):
|
||||||
addr = json_in['addr']
|
addr = json_in['addr']
|
||||||
addr_type = json_in['addr_type']
|
addr_type = json_in['addr_type']
|
||||||
return enc_addr_tlv(addr, addr_type)
|
return enc_addr_tlv(addr, addr_type)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.9
|
# TS 31.103 Section 4.2.9
|
||||||
|
|
||||||
|
|
||||||
class EF_GBABP(TransparentEF):
|
class EF_GBABP(TransparentEF):
|
||||||
def __init__(self, fid='6fd5', sfid=None, name='EF.GBABP', desc='GBA Bootstrapping'):
|
def __init__(self, fid='6fd5', sfid=None, name='EF.GBABP', desc='GBA Bootstrapping'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.10
|
# TS 31.103 Section 4.2.10
|
||||||
|
|
||||||
|
|
||||||
class EF_GBANL(LinFixedEF):
|
class EF_GBANL(LinFixedEF):
|
||||||
def __init__(self, fid='6fd7', sfid=None, name='EF.GBANL', desc='GBA NAF List'):
|
def __init__(self, fid='6fd7', sfid=None, name='EF.GBANL', desc='GBA NAF List'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.11
|
# TS 31.103 Section 4.2.11
|
||||||
|
|
||||||
|
|
||||||
class EF_NAFKCA(LinFixedEF):
|
class EF_NAFKCA(LinFixedEF):
|
||||||
def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', desc='NAF Key Centre Address'):
|
def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', desc='NAF Key Centre Address'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.16
|
# TS 31.103 Section 4.2.16
|
||||||
|
|
||||||
|
|
||||||
class EF_UICCIARI(LinFixedEF):
|
class EF_UICCIARI(LinFixedEF):
|
||||||
class iari(BER_TLV_IE, tag=0x80):
|
class iari(BER_TLV_IE, tag=0x80):
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
|
|
||||||
def __init__(self, fid='6fe7', sfid=None, name='EF.UICCIARI', desc='UICC IARI'):
|
def __init__(self, fid='6fe7', sfid=None, name='EF.UICCIARI', desc='UICC IARI'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_UICCIARI.iari
|
self._tlv = EF_UICCIARI.iari
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.18
|
# TS 31.103 Section 4.2.18
|
||||||
|
|
||||||
|
|
||||||
class EF_IMSConfigData(BerTlvEF):
|
class EF_IMSConfigData(BerTlvEF):
|
||||||
def __init__(self, fid='6ff8', sfid=None, name='EF.IMSConfigData', desc='IMS Configuration Data'):
|
def __init__(self, fid='6ff8', sfid=None, name='EF.IMSConfigData', desc='IMS Configuration Data'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.19
|
# TS 31.103 Section 4.2.19
|
||||||
|
|
||||||
|
|
||||||
class EF_XCAPConfigData(BerTlvEF):
|
class EF_XCAPConfigData(BerTlvEF):
|
||||||
def __init__(self, fid='6ffc', sfid=None, name='EF.XCAPConfigData', desc='XCAP Configuration Data'):
|
def __init__(self, fid='6ffc', sfid=None, name='EF.XCAPConfigData', desc='XCAP Configuration Data'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.20
|
# TS 31.103 Section 4.2.20
|
||||||
|
|
||||||
|
|
||||||
class EF_WebRTCURI(TransparentEF):
|
class EF_WebRTCURI(TransparentEF):
|
||||||
class uri(BER_TLV_IE, tag=0x80):
|
class uri(BER_TLV_IE, tag=0x80):
|
||||||
_construct = GreedyString("utf8")
|
_construct = GreedyString("utf8")
|
||||||
|
|
||||||
def __init__(self, fid='6ffa', sfid=None, name='EF.WebRTCURI', desc='WebRTC URI'):
|
def __init__(self, fid='6ffa', sfid=None, name='EF.WebRTCURI', desc='WebRTC URI'):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_WebRTCURI.uri
|
self._tlv = EF_WebRTCURI.uri
|
||||||
|
|
||||||
# TS 31.103 Section 4.2.21
|
# TS 31.103 Section 4.2.21
|
||||||
|
|
||||||
|
|
||||||
class EF_MuDMiDConfigData(BerTlvEF):
|
class EF_MuDMiDConfigData(BerTlvEF):
|
||||||
def __init__(self, fid='6ffe', sfid=None, name='EF.MuDMiDConfigData',
|
def __init__(self, fid='6ffe', sfid=None, name='EF.MuDMiDConfigData',
|
||||||
desc='MuD and MiD Configuration Data'):
|
desc='MuD and MiD Configuration Data'):
|
||||||
@@ -172,7 +203,8 @@ class ADF_ISIM(CardADF):
|
|||||||
EF_IMPU(),
|
EF_IMPU(),
|
||||||
EF_AD(),
|
EF_AD(),
|
||||||
EF_ARR('6f06', 0x06),
|
EF_ARR('6f06', 0x06),
|
||||||
EF_UServiceTable('6f07', 0x07, 'EF.IST', 'ISIM Service Table', {1,None}, EF_IST_map),
|
EF_UServiceTable('6f07', 0x07, 'EF.IST',
|
||||||
|
'ISIM Service Table', {1, None}, EF_IST_map),
|
||||||
EF_PCSCF(),
|
EF_PCSCF(),
|
||||||
EF_GBABP(),
|
EF_GBABP(),
|
||||||
EF_GBANL(),
|
EF_GBANL(),
|
||||||
@@ -195,6 +227,7 @@ class ADF_ISIM(CardADF):
|
|||||||
def decode_select_response(self, data_hex):
|
def decode_select_response(self, data_hex):
|
||||||
return pySim.ts_102_221.CardProfileUICC.decode_select_response(data_hex)
|
return pySim.ts_102_221.CardProfileUICC.decode_select_response(data_hex)
|
||||||
|
|
||||||
|
|
||||||
# TS 31.103 Section 7.1
|
# TS 31.103 Section 7.1
|
||||||
sw_isim = {
|
sw_isim = {
|
||||||
'Security management': {
|
'Security management': {
|
||||||
@@ -203,6 +236,7 @@ sw_isim = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CardApplicationISIM(CardApplication):
|
class CardApplicationISIM(CardApplication):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('ISIM', adf=ADF_ISIM(), sw=sw_isim)
|
super().__init__('ISIM', adf=ADF_ISIM(), sw=sw_isim)
|
||||||
|
|||||||
@@ -29,6 +29,17 @@ order to describe the files specified in the relevant ETSI + 3GPP specifications
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from pySim.profile import match_sim
|
||||||
|
from pySim.profile import CardProfile
|
||||||
|
from pySim.filesystem import *
|
||||||
|
import enum
|
||||||
|
from pySim.construct import *
|
||||||
|
from construct import Optional as COptional
|
||||||
|
from construct import *
|
||||||
|
from struct import pack, unpack
|
||||||
|
from typing import Tuple
|
||||||
|
from pySim.tlv import *
|
||||||
|
from pySim.utils import *
|
||||||
MF_num = '3F00'
|
MF_num = '3F00'
|
||||||
|
|
||||||
DF_num = {
|
DF_num = {
|
||||||
@@ -323,27 +334,18 @@ EF_SST_map = {
|
|||||||
59: 'MMS User Connectivity Parameters',
|
59: 'MMS User Connectivity Parameters',
|
||||||
}
|
}
|
||||||
|
|
||||||
from pySim.utils import *
|
|
||||||
from pySim.tlv import *
|
|
||||||
from typing import Tuple
|
|
||||||
from struct import pack, unpack
|
|
||||||
from construct import *
|
|
||||||
from construct import Optional as COptional
|
|
||||||
from pySim.construct import *
|
|
||||||
import enum
|
|
||||||
|
|
||||||
from pySim.filesystem import *
|
|
||||||
from pySim.profile import CardProfile
|
|
||||||
from pySim.profile import match_sim
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# DF.TELECOM
|
# DF.TELECOM
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
# TS 51.011 Section 10.5.1
|
# TS 51.011 Section 10.5.1
|
||||||
|
|
||||||
class EF_ADN(LinFixedEF):
|
class EF_ADN(LinFixedEF):
|
||||||
def __init__(self, fid='6f3a', sfid=None, name='EF.ADN', desc='Abbreviated Dialing Numbers'):
|
def __init__(self, fid='6f3a', sfid=None, name='EF.ADN', desc='Abbreviated Dialing Numbers'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={14, 30})
|
super().__init__(fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len={14, 30})
|
||||||
|
|
||||||
def _decode_record_bin(self, raw_bin_data):
|
def _decode_record_bin(self, raw_bin_data):
|
||||||
alpha_id_len = len(raw_bin_data) - 14
|
alpha_id_len = len(raw_bin_data) - 14
|
||||||
alpha_id = raw_bin_data[:alpha_id_len]
|
alpha_id = raw_bin_data[:alpha_id_len]
|
||||||
@@ -352,9 +354,13 @@ class EF_ADN(LinFixedEF):
|
|||||||
'dialing_nr': u[2], 'cap_conf_id': u[3], 'ext1_record_id': u[4]}
|
'dialing_nr': u[2], 'cap_conf_id': u[3], 'ext1_record_id': u[4]}
|
||||||
|
|
||||||
# TS 51.011 Section 10.5.5
|
# TS 51.011 Section 10.5.5
|
||||||
|
|
||||||
|
|
||||||
class EF_SMS(LinFixedEF):
|
class EF_SMS(LinFixedEF):
|
||||||
def __init__(self, fid='6f3c', sfid=None, name='EF.SMS', desc='Short messages'):
|
def __init__(self, fid='6f3c', sfid=None, name='EF.SMS', desc='Short messages'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={176,176})
|
super().__init__(fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len={176, 176})
|
||||||
|
|
||||||
def _decode_record_bin(self, raw_bin_data):
|
def _decode_record_bin(self, raw_bin_data):
|
||||||
def decode_status(status):
|
def decode_status(status):
|
||||||
if status & 0x01 == 0x00:
|
if status & 0x01 == 0x00:
|
||||||
@@ -384,51 +390,72 @@ class EF_SMS(LinFixedEF):
|
|||||||
# TS 51.011 Section 10.5.5
|
# TS 51.011 Section 10.5.5
|
||||||
class EF_MSISDN(LinFixedEF):
|
class EF_MSISDN(LinFixedEF):
|
||||||
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN'):
|
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={15, 34})
|
super().__init__(fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len={15, 34})
|
||||||
|
|
||||||
def _decode_record_hex(self, raw_hex_data):
|
def _decode_record_hex(self, raw_hex_data):
|
||||||
return {'msisdn': dec_msisdn(raw_hex_data)}
|
return {'msisdn': dec_msisdn(raw_hex_data)}
|
||||||
|
|
||||||
def _encode_record_hex(self, abstract):
|
def _encode_record_hex(self, abstract):
|
||||||
msisdn = abstract['msisdn']
|
msisdn = abstract['msisdn']
|
||||||
if type(msisdn) == str:
|
if type(msisdn) == str:
|
||||||
encoded_msisdn = enc_msisdn(msisdn)
|
encoded_msisdn = enc_msisdn(msisdn)
|
||||||
else:
|
else:
|
||||||
encoded_msisdn = enc_msisdn(msisdn[2], msisdn[0], msisdn[1])
|
encoded_msisdn = enc_msisdn(msisdn[2], msisdn[0], msisdn[1])
|
||||||
alpha_identifier = (list(self.rec_len)[0] - len(encoded_msisdn) // 2) * "ff"
|
alpha_identifier = (list(self.rec_len)[
|
||||||
|
0] - len(encoded_msisdn) // 2) * "ff"
|
||||||
return alpha_identifier + encoded_msisdn
|
return alpha_identifier + encoded_msisdn
|
||||||
|
|
||||||
# TS 51.011 Section 10.5.6
|
# TS 51.011 Section 10.5.6
|
||||||
|
|
||||||
|
|
||||||
class EF_SMSP(LinFixedEF):
|
class EF_SMSP(LinFixedEF):
|
||||||
def __init__(self, fid='6f42', sfid=None, name='EF.SMSP', desc='Short message service parameters'):
|
def __init__(self, fid='6f42', sfid=None, name='EF.SMSP', desc='Short message service parameters'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={28, None})
|
super().__init__(fid, sfid=sfid, name=name,
|
||||||
|
desc=desc, rec_len={28, None})
|
||||||
|
|
||||||
# TS 51.011 Section 10.5.7
|
# TS 51.011 Section 10.5.7
|
||||||
|
|
||||||
|
|
||||||
class EF_SMSS(TransparentEF):
|
class EF_SMSS(TransparentEF):
|
||||||
class MemCapAdapter(Adapter):
|
class MemCapAdapter(Adapter):
|
||||||
def _decode(self, obj, context, path):
|
def _decode(self, obj, context, path):
|
||||||
return False if obj & 1 else True
|
return False if obj & 1 else True
|
||||||
|
|
||||||
def _encode(self, obj, context, path):
|
def _encode(self, obj, context, path):
|
||||||
return 0 if obj else 1
|
return 0 if obj else 1
|
||||||
|
|
||||||
def __init__(self, fid='6f43', sfid=None, name='EF.SMSS', desc='SMS status', size={2, 8}):
|
def __init__(self, fid='6f43', sfid=None, name='EF.SMSS', desc='SMS status', size={2, 8}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self._construct = Struct('last_used_tpmr'/Int8ub, 'memory_capacity_exceeded'/self.MemCapAdapter(Int8ub))
|
self._construct = Struct(
|
||||||
|
'last_used_tpmr'/Int8ub, 'memory_capacity_exceeded'/self.MemCapAdapter(Int8ub))
|
||||||
|
|
||||||
# TS 51.011 Section 10.5.8
|
# TS 51.011 Section 10.5.8
|
||||||
|
|
||||||
|
|
||||||
class EF_SMSR(LinFixedEF):
|
class EF_SMSR(LinFixedEF):
|
||||||
def __init__(self, fid='6f47', sfid=None, name='EF.SMSR', desc='SMS status reports', rec_len={30, 30}):
|
def __init__(self, fid='6f47', sfid=None, name='EF.SMSR', desc='SMS status reports', rec_len={30, 30}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
self._construct = Struct('sms_record_id'/Int8ub, 'sms_status_report'/HexAdapter(Bytes(29)))
|
self._construct = Struct(
|
||||||
|
'sms_record_id'/Int8ub, 'sms_status_report'/HexAdapter(Bytes(29)))
|
||||||
|
|
||||||
|
|
||||||
class EF_EXT(LinFixedEF):
|
class EF_EXT(LinFixedEF):
|
||||||
def __init__(self, fid, sfid=None, name='EF.EXT', desc='Extension', rec_len={13, 13}):
|
def __init__(self, fid, sfid=None, name='EF.EXT', desc='Extension', rec_len={13, 13}):
|
||||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
self._construct = Struct('record_type'/Int8ub, 'extension_data'/HexAdapter(Bytes(11)), 'identifier'/Int8ub)
|
self._construct = Struct(
|
||||||
|
'record_type'/Int8ub, 'extension_data'/HexAdapter(Bytes(11)), 'identifier'/Int8ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.5.16
|
# TS 51.011 Section 10.5.16
|
||||||
|
|
||||||
|
|
||||||
class EF_CMI(LinFixedEF):
|
class EF_CMI(LinFixedEF):
|
||||||
def __init__(self, fid='6f58', sfid=None, name='EF.CMI', rec_len={2, 21},
|
def __init__(self, fid='6f58', sfid=None, name='EF.CMI', rec_len={2, 21},
|
||||||
desc='Comparison Method Information'):
|
desc='Comparison Method Information'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
self._construct = Struct('alpha_id'/Bytes(this._.total_len-1), 'comparison_method_id'/Int8ub)
|
self._construct = Struct(
|
||||||
|
'alpha_id'/Bytes(this._.total_len-1), 'comparison_method_id'/Int8ub)
|
||||||
|
|
||||||
|
|
||||||
class DF_TELECOM(CardDF):
|
class DF_TELECOM(CardDF):
|
||||||
def __init__(self, fid='7f10', name='DF.TELECOM', desc=None):
|
def __init__(self, fid='7f10', name='DF.TELECOM', desc=None):
|
||||||
@@ -437,8 +464,10 @@ class DF_TELECOM(CardDF):
|
|||||||
EF_ADN(),
|
EF_ADN(),
|
||||||
EF_ADN(fid='6f3b', name='EF.FDN', desc='Fixed dialling numbers'),
|
EF_ADN(fid='6f3b', name='EF.FDN', desc='Fixed dialling numbers'),
|
||||||
EF_SMS(),
|
EF_SMS(),
|
||||||
LinFixedEF(fid='6f3d', name='EF.CCP', desc='Capability Configuration Parameters', rec_len={14,14}),
|
LinFixedEF(fid='6f3d', name='EF.CCP',
|
||||||
LinFixedEF(fid='6f4f', name='EF.ECCP', desc='Extended Capability Configuration Parameters', rec_len={15,32}),
|
desc='Capability Configuration Parameters', rec_len={14, 14}),
|
||||||
|
LinFixedEF(fid='6f4f', name='EF.ECCP',
|
||||||
|
desc='Extended Capability Configuration Parameters', rec_len={15, 32}),
|
||||||
EF_MSISDN(),
|
EF_MSISDN(),
|
||||||
EF_SMSP(),
|
EF_SMSP(),
|
||||||
EF_SMSS(),
|
EF_SMSS(),
|
||||||
@@ -458,25 +487,34 @@ class DF_TELECOM(CardDF):
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.1
|
# TS 51.011 Section 10.3.1
|
||||||
|
|
||||||
|
|
||||||
class EF_LP(TransRecEF):
|
class EF_LP(TransRecEF):
|
||||||
def __init__(self, fid='6f05', sfid=None, name='EF.LP', size={1, None}, rec_len=1,
|
def __init__(self, fid='6f05', sfid=None, name='EF.LP', size={1, None}, rec_len=1,
|
||||||
desc='Language Preference'):
|
desc='Language Preference'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
||||||
|
|
||||||
def _decode_record_bin(self, in_bin):
|
def _decode_record_bin(self, in_bin):
|
||||||
return b2h(in_bin)
|
return b2h(in_bin)
|
||||||
|
|
||||||
def _encode_record_bin(self, in_json):
|
def _encode_record_bin(self, in_json):
|
||||||
return h2b(in_json)
|
return h2b(in_json)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.2
|
# TS 51.011 Section 10.3.2
|
||||||
|
|
||||||
|
|
||||||
class EF_IMSI(TransparentEF):
|
class EF_IMSI(TransparentEF):
|
||||||
def __init__(self, fid='6f07', sfid=None, name='EF.IMSI', desc='IMSI', size={9, 9}):
|
def __init__(self, fid='6f07', sfid=None, name='EF.IMSI', desc='IMSI', size={9, 9}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
# add those commands to the general commands of a TransparentEF
|
# add those commands to the general commands of a TransparentEF
|
||||||
self.shell_commands += [self.AddlShellCommands(self)]
|
self.shell_commands += [self.AddlShellCommands(self)]
|
||||||
|
|
||||||
def _decode_hex(self, raw_hex):
|
def _decode_hex(self, raw_hex):
|
||||||
return {'imsi': dec_imsi(raw_hex)}
|
return {'imsi': dec_imsi(raw_hex)}
|
||||||
|
|
||||||
def _encode_hex(self, abstract):
|
def _encode_hex(self, abstract):
|
||||||
return enc_imsi(abstract['imsi'])
|
return enc_imsi(abstract['imsi'])
|
||||||
|
|
||||||
@with_default_category('File-Specific Commands')
|
@with_default_category('File-Specific Commands')
|
||||||
class AddlShellCommands(CommandSet):
|
class AddlShellCommands(CommandSet):
|
||||||
def __init__(self, ef: TransparentEF):
|
def __init__(self, ef: TransparentEF):
|
||||||
@@ -491,9 +529,11 @@ class EF_IMSI(TransparentEF):
|
|||||||
if sw == '9000' and len(data['imsi'])-len(plmn) == 10:
|
if sw == '9000' and len(data['imsi'])-len(plmn) == 10:
|
||||||
imsi = data['imsi']
|
imsi = data['imsi']
|
||||||
msin = imsi[len(plmn):]
|
msin = imsi[len(plmn):]
|
||||||
(data, sw) = self._cmd.rs.update_binary_dec({'imsi': plmn+msin})
|
(data, sw) = self._cmd.rs.update_binary_dec(
|
||||||
|
{'imsi': plmn+msin})
|
||||||
if sw == '9000' and data:
|
if sw == '9000' and data:
|
||||||
self._cmd.poutput_json(self._cmd.rs.selected_file.decode_hex(data))
|
self._cmd.poutput_json(
|
||||||
|
self._cmd.rs.selected_file.decode_hex(data))
|
||||||
else:
|
else:
|
||||||
raise ValueError("PLMN length does not match IMSI length")
|
raise ValueError("PLMN length does not match IMSI length")
|
||||||
else:
|
else:
|
||||||
@@ -505,11 +545,13 @@ class EF_PLMNsel(TransRecEF):
|
|||||||
def __init__(self, fid='6f30', sfid=None, name='EF.PLMNsel', desc='PLMN selector',
|
def __init__(self, fid='6f30', sfid=None, name='EF.PLMNsel', desc='PLMN selector',
|
||||||
size={24, None}, rec_len=3):
|
size={24, None}, rec_len=3):
|
||||||
super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len)
|
super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len)
|
||||||
|
|
||||||
def _decode_record_hex(self, in_hex):
|
def _decode_record_hex(self, in_hex):
|
||||||
if in_hex[:6] == "ffffff":
|
if in_hex[:6] == "ffffff":
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return dec_plmn(in_hex)
|
return dec_plmn(in_hex)
|
||||||
|
|
||||||
def _encode_record_hex(self, in_json):
|
def _encode_record_hex(self, in_json):
|
||||||
if in_json == None:
|
if in_json == None:
|
||||||
return "ffffff"
|
return "ffffff"
|
||||||
@@ -517,6 +559,8 @@ class EF_PLMNsel(TransRecEF):
|
|||||||
return enc_plmn(in_json['mcc'], in_json['mnc'])
|
return enc_plmn(in_json['mcc'], in_json['mnc'])
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.6
|
# TS 51.011 Section 10.3.6
|
||||||
|
|
||||||
|
|
||||||
class EF_ACMmax(TransparentEF):
|
class EF_ACMmax(TransparentEF):
|
||||||
def __init__(self, fid='6f37', sfid=None, name='EF.ACMmax', size={3, 3},
|
def __init__(self, fid='6f37', sfid=None, name='EF.ACMmax', size={3, 3},
|
||||||
desc='ACM maximum value'):
|
desc='ACM maximum value'):
|
||||||
@@ -524,16 +568,20 @@ class EF_ACMmax(TransparentEF):
|
|||||||
self._construct = Struct('acm_max'/Int24ub)
|
self._construct = Struct('acm_max'/Int24ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.7
|
# TS 51.011 Section 10.3.7
|
||||||
|
|
||||||
|
|
||||||
class EF_ServiceTable(TransparentEF):
|
class EF_ServiceTable(TransparentEF):
|
||||||
def __init__(self, fid, sfid, name, desc, size, table):
|
def __init__(self, fid, sfid, name, desc, size, table):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self.table = table
|
self.table = table
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]:
|
def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]:
|
||||||
i = service - 1
|
i = service - 1
|
||||||
byte_offset = i//4
|
byte_offset = i//4
|
||||||
bit_offset = (i % 4) * 2
|
bit_offset = (i % 4) * 2
|
||||||
return (byte_offset, bit_offset)
|
return (byte_offset, bit_offset)
|
||||||
|
|
||||||
def _decode_bin(self, raw_bin):
|
def _decode_bin(self, raw_bin):
|
||||||
ret = {}
|
ret = {}
|
||||||
for i in range(0, len(raw_bin)*4):
|
for i in range(0, len(raw_bin)*4):
|
||||||
@@ -547,19 +595,22 @@ class EF_ServiceTable(TransparentEF):
|
|||||||
'activated': True if bits & 2 else False,
|
'activated': True if bits & 2 else False,
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _encode_bin(self, in_json):
|
def _encode_bin(self, in_json):
|
||||||
# compute the required binary size
|
# compute the required binary size
|
||||||
bin_len = 0
|
bin_len = 0
|
||||||
for srv in in_json.keys():
|
for srv in in_json.keys():
|
||||||
service_nr = int(srv)
|
service_nr = int(srv)
|
||||||
(byte_offset, bit_offset) = EF_ServiceTable._bit_byte_offset_for_service(service_nr)
|
(byte_offset, bit_offset) = EF_ServiceTable._bit_byte_offset_for_service(
|
||||||
|
service_nr)
|
||||||
if byte_offset >= bin_len:
|
if byte_offset >= bin_len:
|
||||||
bin_len = byte_offset+1
|
bin_len = byte_offset+1
|
||||||
# encode the actual data
|
# encode the actual data
|
||||||
out = bytearray(b'\x00' * bin_len)
|
out = bytearray(b'\x00' * bin_len)
|
||||||
for srv in in_json.keys():
|
for srv in in_json.keys():
|
||||||
service_nr = int(srv)
|
service_nr = int(srv)
|
||||||
(byte_offset, bit_offset) = EF_ServiceTable._bit_byte_offset_for_service(service_nr)
|
(byte_offset, bit_offset) = EF_ServiceTable._bit_byte_offset_for_service(
|
||||||
|
service_nr)
|
||||||
bits = 0
|
bits = 0
|
||||||
if in_json[srv]['allocated'] == True:
|
if in_json[srv]['allocated'] == True:
|
||||||
bits |= 1
|
bits |= 1
|
||||||
@@ -569,6 +620,8 @@ class EF_ServiceTable(TransparentEF):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.11
|
# TS 51.011 Section 10.3.11
|
||||||
|
|
||||||
|
|
||||||
class EF_SPN(TransparentEF):
|
class EF_SPN(TransparentEF):
|
||||||
def __init__(self, fid='6f46', sfid=None, name='EF.SPN', desc='Service Provider Name', size={17, 17}):
|
def __init__(self, fid='6f46', sfid=None, name='EF.SPN', desc='Service Provider Name', size={17, 17}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
@@ -582,6 +635,8 @@ class EF_SPN(TransparentEF):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.13
|
# TS 51.011 Section 10.3.13
|
||||||
|
|
||||||
|
|
||||||
class EF_CBMI(TransRecEF):
|
class EF_CBMI(TransRecEF):
|
||||||
def __init__(self, fid='6f45', sfid=None, name='EF.CBMI', size={2, None}, rec_len=2,
|
def __init__(self, fid='6f45', sfid=None, name='EF.CBMI', size={2, None}, rec_len=2,
|
||||||
desc='Cell Broadcast message identifier selection'):
|
desc='Cell Broadcast message identifier selection'):
|
||||||
@@ -589,15 +644,21 @@ class EF_CBMI(TransRecEF):
|
|||||||
self._construct = GreedyRange(Int16ub)
|
self._construct = GreedyRange(Int16ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.15
|
# TS 51.011 Section 10.3.15
|
||||||
|
|
||||||
|
|
||||||
class EF_ACC(TransparentEF):
|
class EF_ACC(TransparentEF):
|
||||||
def __init__(self, fid='6f78', sfid=None, name='EF.ACC', desc='Access Control Class', size={2, 2}):
|
def __init__(self, fid='6f78', sfid=None, name='EF.ACC', desc='Access Control Class', size={2, 2}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
|
|
||||||
def _decode_bin(self, raw_bin):
|
def _decode_bin(self, raw_bin):
|
||||||
return {'acc': unpack('!H', raw_bin)[0]}
|
return {'acc': unpack('!H', raw_bin)[0]}
|
||||||
|
|
||||||
def _encode_bin(self, abstract):
|
def _encode_bin(self, abstract):
|
||||||
return pack('!H', abstract['acc'])
|
return pack('!H', abstract['acc'])
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.16
|
# TS 51.011 Section 10.3.16
|
||||||
|
|
||||||
|
|
||||||
class EF_LOCI(TransparentEF):
|
class EF_LOCI(TransparentEF):
|
||||||
def __init__(self, fid='6f7e', sfid=None, name='EF.LOCI', desc='Location Information', size={11, 11}):
|
def __init__(self, fid='6f7e', sfid=None, name='EF.LOCI', desc='Location Information', size={11, 11}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
@@ -606,6 +667,8 @@ class EF_LOCI(TransparentEF):
|
|||||||
location_area_not_allowed=3))
|
location_area_not_allowed=3))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.18
|
# TS 51.011 Section 10.3.18
|
||||||
|
|
||||||
|
|
||||||
class EF_AD(TransparentEF):
|
class EF_AD(TransparentEF):
|
||||||
class OP_MODE(enum.IntEnum):
|
class OP_MODE(enum.IntEnum):
|
||||||
normal = 0x00
|
normal = 0x00
|
||||||
@@ -637,6 +700,8 @@ class EF_AD(TransparentEF):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.20 / 10.3.22
|
# TS 51.011 Section 10.3.20 / 10.3.22
|
||||||
|
|
||||||
|
|
||||||
class EF_VGCS(TransRecEF):
|
class EF_VGCS(TransRecEF):
|
||||||
def __init__(self, fid='6fb1', sfid=None, name='EF.VGCS', size={4, 200}, rec_len=4,
|
def __init__(self, fid='6fb1', sfid=None, name='EF.VGCS', size={4, 200}, rec_len=4,
|
||||||
desc='Voice Group Call Service'):
|
desc='Voice Group Call Service'):
|
||||||
@@ -644,29 +709,41 @@ class EF_VGCS(TransRecEF):
|
|||||||
self._construct = BcdAdapter(Bytes(4))
|
self._construct = BcdAdapter(Bytes(4))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.21 / 10.3.23
|
# TS 51.011 Section 10.3.21 / 10.3.23
|
||||||
|
|
||||||
|
|
||||||
class EF_VGCSS(TransparentEF):
|
class EF_VGCSS(TransparentEF):
|
||||||
def __init__(self, fid='6fb2', sfid=None, name='EF.VGCSS', size={7, 7},
|
def __init__(self, fid='6fb2', sfid=None, name='EF.VGCSS', size={7, 7},
|
||||||
desc='Voice Group Call Service Status'):
|
desc='Voice Group Call Service Status'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self._construct = BitStruct('flags'/Bit[50], Padding(6, pattern=b'\xff'))
|
self._construct = BitStruct(
|
||||||
|
'flags'/Bit[50], Padding(6, pattern=b'\xff'))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.24
|
# TS 51.011 Section 10.3.24
|
||||||
|
|
||||||
|
|
||||||
class EF_eMLPP(TransparentEF):
|
class EF_eMLPP(TransparentEF):
|
||||||
def __init__(self, fid='6fb5', sfid=None, name='EF.eMLPP', size={2, 2},
|
def __init__(self, fid='6fb5', sfid=None, name='EF.eMLPP', size={2, 2},
|
||||||
desc='enhanced Multi Level Pre-emption and Priority'):
|
desc='enhanced Multi Level Pre-emption and Priority'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
FlagsConstruct = FlagsEnum(Byte, A=1, B=2, zero=4, one=8, two=16, three=32, four=64)
|
FlagsConstruct = FlagsEnum(
|
||||||
self._construct = Struct('levels'/FlagsConstruct, 'fast_call_setup_cond'/FlagsConstruct)
|
Byte, A=1, B=2, zero=4, one=8, two=16, three=32, four=64)
|
||||||
|
self._construct = Struct(
|
||||||
|
'levels'/FlagsConstruct, 'fast_call_setup_cond'/FlagsConstruct)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.25
|
# TS 51.011 Section 10.3.25
|
||||||
|
|
||||||
|
|
||||||
class EF_AAeM(TransparentEF):
|
class EF_AAeM(TransparentEF):
|
||||||
def __init__(self, fid='6fb6', sfid=None, name='EF.AAeM', size={1, 1},
|
def __init__(self, fid='6fb6', sfid=None, name='EF.AAeM', size={1, 1},
|
||||||
desc='Automatic Answer for eMLPP Service'):
|
desc='Automatic Answer for eMLPP Service'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
FlagsConstruct = FlagsEnum(Byte, A=1, B=2, zero=4, one=8, two=16, three=32, four=64)
|
FlagsConstruct = FlagsEnum(
|
||||||
|
Byte, A=1, B=2, zero=4, one=8, two=16, three=32, four=64)
|
||||||
self._construct = Struct('auto_answer_prio_levels'/FlagsConstruct)
|
self._construct = Struct('auto_answer_prio_levels'/FlagsConstruct)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.26
|
# TS 51.011 Section 10.3.26
|
||||||
|
|
||||||
|
|
||||||
class EF_CBMID(EF_CBMI):
|
class EF_CBMID(EF_CBMI):
|
||||||
def __init__(self, fid='6f48', sfid=None, name='EF.CBMID', size={2, None}, rec_len=2,
|
def __init__(self, fid='6f48', sfid=None, name='EF.CBMID', size={2, None}, rec_len=2,
|
||||||
desc='Cell Broadcast Message Identifier for Data Download'):
|
desc='Cell Broadcast Message Identifier for Data Download'):
|
||||||
@@ -674,6 +751,8 @@ class EF_CBMID(EF_CBMI):
|
|||||||
self._construct = GreedyRange(Int16ub)
|
self._construct = GreedyRange(Int16ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.27
|
# TS 51.011 Section 10.3.27
|
||||||
|
|
||||||
|
|
||||||
class EF_ECC(TransRecEF):
|
class EF_ECC(TransRecEF):
|
||||||
def __init__(self, fid='6fb7', sfid=None, name='EF.ECC', size={3, 15}, rec_len=3,
|
def __init__(self, fid='6fb7', sfid=None, name='EF.ECC', size={3, 15}, rec_len=3,
|
||||||
desc='Emergency Call Codes'):
|
desc='Emergency Call Codes'):
|
||||||
@@ -681,6 +760,8 @@ class EF_ECC(TransRecEF):
|
|||||||
self._construct = GreedyRange(BcdAdapter(Bytes(3)))
|
self._construct = GreedyRange(BcdAdapter(Bytes(3)))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.28
|
# TS 51.011 Section 10.3.28
|
||||||
|
|
||||||
|
|
||||||
class EF_CBMIR(TransRecEF):
|
class EF_CBMIR(TransRecEF):
|
||||||
def __init__(self, fid='6f50', sfid=None, name='EF.CBMIR', size={4, None}, rec_len=4,
|
def __init__(self, fid='6f50', sfid=None, name='EF.CBMIR', size={4, None}, rec_len=4,
|
||||||
desc='Cell Broadcast message identifier range selection'):
|
desc='Cell Broadcast message identifier range selection'):
|
||||||
@@ -688,6 +769,8 @@ class EF_CBMIR(TransRecEF):
|
|||||||
self._construct = GreedyRange(Struct('lower'/Int16ub, 'upper'/Int16ub))
|
self._construct = GreedyRange(Struct('lower'/Int16ub, 'upper'/Int16ub))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.29
|
# TS 51.011 Section 10.3.29
|
||||||
|
|
||||||
|
|
||||||
class EF_DCK(TransparentEF):
|
class EF_DCK(TransparentEF):
|
||||||
def __init__(self, fid='6f2c', sfid=None, name='EF.DCK', size={16, 16},
|
def __init__(self, fid='6f2c', sfid=None, name='EF.DCK', size={16, 16},
|
||||||
desc='Depersonalisation Control Keys'):
|
desc='Depersonalisation Control Keys'):
|
||||||
@@ -697,10 +780,13 @@ class EF_DCK(TransparentEF):
|
|||||||
'service_provider'/BcdAdapter(Bytes(4)),
|
'service_provider'/BcdAdapter(Bytes(4)),
|
||||||
'corporate'/BcdAdapter(Bytes(4)))
|
'corporate'/BcdAdapter(Bytes(4)))
|
||||||
# TS 51.011 Section 10.3.30
|
# TS 51.011 Section 10.3.30
|
||||||
|
|
||||||
|
|
||||||
class EF_CNL(TransRecEF):
|
class EF_CNL(TransRecEF):
|
||||||
def __init__(self, fid='6f32', sfid=None, name='EF.CNL', size={6, None}, rec_len=6,
|
def __init__(self, fid='6f32', sfid=None, name='EF.CNL', size={6, None}, rec_len=6,
|
||||||
desc='Co-operative Network List'):
|
desc='Co-operative Network List'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
||||||
|
|
||||||
def _decode_record_hex(self, in_hex):
|
def _decode_record_hex(self, in_hex):
|
||||||
(in_plmn, sub, svp, corp) = unpack('!3sBBB', h2b(in_hex))
|
(in_plmn, sub, svp, corp) = unpack('!3sBBB', h2b(in_hex))
|
||||||
res = dec_plmn(b2h(in_plmn))
|
res = dec_plmn(b2h(in_plmn))
|
||||||
@@ -708,6 +794,7 @@ class EF_CNL(TransRecEF):
|
|||||||
res['service_provider_id'] = svp
|
res['service_provider_id'] = svp
|
||||||
res['corporate_id'] = corp
|
res['corporate_id'] = corp
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _encode_record_hex(self, in_json):
|
def _encode_record_hex(self, in_json):
|
||||||
plmn = enc_plmn(in_json['mcc'], in_json['mnc'])
|
plmn = enc_plmn(in_json['mcc'], in_json['mnc'])
|
||||||
return b2h(pack('!3sBBB',
|
return b2h(pack('!3sBBB',
|
||||||
@@ -717,19 +804,26 @@ class EF_CNL(TransRecEF):
|
|||||||
in_json['corporate_id']))
|
in_json['corporate_id']))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.31
|
# TS 51.011 Section 10.3.31
|
||||||
|
|
||||||
|
|
||||||
class EF_NIA(LinFixedEF):
|
class EF_NIA(LinFixedEF):
|
||||||
def __init__(self, fid='6f51', sfid=None, name='EF.NIA', rec_len={1, 32},
|
def __init__(self, fid='6f51', sfid=None, name='EF.NIA', rec_len={1, 32},
|
||||||
desc='Network\'s Indication of Alerting'):
|
desc='Network\'s Indication of Alerting'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
self._construct = Struct('alerting_category'/Int8ub, 'category'/GreedyBytes)
|
self._construct = Struct(
|
||||||
|
'alerting_category'/Int8ub, 'category'/GreedyBytes)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.32
|
# TS 51.011 Section 10.3.32
|
||||||
|
|
||||||
|
|
||||||
class EF_Kc(TransparentEF):
|
class EF_Kc(TransparentEF):
|
||||||
def __init__(self, fid='6f20', sfid=None, name='EF.Kc', desc='Ciphering key Kc', size={9, 9}):
|
def __init__(self, fid='6f20', sfid=None, name='EF.Kc', desc='Ciphering key Kc', size={9, 9}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self._construct = Struct('kc'/HexAdapter(Bytes(8)), 'cksn'/Int8ub)
|
self._construct = Struct('kc'/HexAdapter(Bytes(8)), 'cksn'/Int8ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.33
|
# TS 51.011 Section 10.3.33
|
||||||
|
|
||||||
|
|
||||||
class EF_LOCIGPRS(TransparentEF):
|
class EF_LOCIGPRS(TransparentEF):
|
||||||
def __init__(self, fid='6f53', sfid=None, name='EF.LOCIGPRS', desc='GPRS Location Information', size={14, 14}):
|
def __init__(self, fid='6f53', sfid=None, name='EF.LOCIGPRS', desc='GPRS Location Information', size={14, 14}):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
@@ -738,14 +832,18 @@ class EF_LOCIGPRS(TransparentEF):
|
|||||||
routing_area_not_allowed=3))
|
routing_area_not_allowed=3))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.35..37
|
# TS 51.011 Section 10.3.35..37
|
||||||
|
|
||||||
|
|
||||||
class EF_xPLMNwAcT(TransRecEF):
|
class EF_xPLMNwAcT(TransRecEF):
|
||||||
def __init__(self, fid, sfid=None, name=None, desc=None, size={40, None}, rec_len=5):
|
def __init__(self, fid, sfid=None, name=None, desc=None, size={40, None}, rec_len=5):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
||||||
|
|
||||||
def _decode_record_hex(self, in_hex):
|
def _decode_record_hex(self, in_hex):
|
||||||
if in_hex[:6] == "ffffff":
|
if in_hex[:6] == "ffffff":
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return dec_xplmn_w_act(in_hex)
|
return dec_xplmn_w_act(in_hex)
|
||||||
|
|
||||||
def _encode_record_hex(self, in_json):
|
def _encode_record_hex(self, in_json):
|
||||||
if in_json == None:
|
if in_json == None:
|
||||||
return "ffffff0000"
|
return "ffffff0000"
|
||||||
@@ -753,6 +851,7 @@ class EF_xPLMNwAcT(TransRecEF):
|
|||||||
hplmn = enc_plmn(in_json['mcc'], in_json['mnc'])
|
hplmn = enc_plmn(in_json['mcc'], in_json['mnc'])
|
||||||
act = self.enc_act(in_json['act'])
|
act = self.enc_act(in_json['act'])
|
||||||
return hplmn + act
|
return hplmn + act
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def enc_act(in_list):
|
def enc_act(in_list):
|
||||||
u16 = 0
|
u16 = 0
|
||||||
@@ -784,6 +883,8 @@ class EF_xPLMNwAcT(TransRecEF):
|
|||||||
return '%04X' % (u16)
|
return '%04X' % (u16)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.38
|
# TS 51.011 Section 10.3.38
|
||||||
|
|
||||||
|
|
||||||
class EF_CPBCCH(TransRecEF):
|
class EF_CPBCCH(TransRecEF):
|
||||||
def __init__(self, fid='6f63', sfid=None, name='EF.CPBCCH', size={2, 14}, rec_len=2,
|
def __init__(self, fid='6f63', sfid=None, name='EF.CPBCCH', size={2, 14}, rec_len=2,
|
||||||
desc='CPBCCH Information'):
|
desc='CPBCCH Information'):
|
||||||
@@ -791,33 +892,45 @@ class EF_CPBCCH(TransRecEF):
|
|||||||
self._construct = Struct('cpbcch'/Int16ub)
|
self._construct = Struct('cpbcch'/Int16ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.39
|
# TS 51.011 Section 10.3.39
|
||||||
|
|
||||||
|
|
||||||
class EF_InvScan(TransparentEF):
|
class EF_InvScan(TransparentEF):
|
||||||
def __init__(self, fid='6f64', sfid=None, name='EF.InvScan', size={1, 1},
|
def __init__(self, fid='6f64', sfid=None, name='EF.InvScan', size={1, 1},
|
||||||
desc='IOnvestigation Scan'):
|
desc='IOnvestigation Scan'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
|
||||||
self._construct = FlagsEnum(Byte, in_limited_service_mode=1, after_successful_plmn_selection=2)
|
self._construct = FlagsEnum(
|
||||||
|
Byte, in_limited_service_mode=1, after_successful_plmn_selection=2)
|
||||||
|
|
||||||
# TS 51.011 Section 4.2.58
|
# TS 51.011 Section 4.2.58
|
||||||
|
|
||||||
|
|
||||||
class EF_PNN(LinFixedEF):
|
class EF_PNN(LinFixedEF):
|
||||||
class FullNameForNetwork(BER_TLV_IE, tag=0x43):
|
class FullNameForNetwork(BER_TLV_IE, tag=0x43):
|
||||||
# TS 24.008 10.5.3.5a
|
# TS 24.008 10.5.3.5a
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ShortNameForNetwork(BER_TLV_IE, tag=0x45):
|
class ShortNameForNetwork(BER_TLV_IE, tag=0x45):
|
||||||
# TS 24.008 10.5.3.5a
|
# TS 24.008 10.5.3.5a
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class NetworkNameCollection(TLV_IE_Collection, nested=[FullNameForNetwork, ShortNameForNetwork]):
|
class NetworkNameCollection(TLV_IE_Collection, nested=[FullNameForNetwork, ShortNameForNetwork]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, fid='6fc5', sfid=None, name='EF.PNN', desc='PLMN Network Name'):
|
def __init__(self, fid='6fc5', sfid=None, name='EF.PNN', desc='PLMN Network Name'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc)
|
||||||
self._tlv = EF_PNN.NetworkNameCollection
|
self._tlv = EF_PNN.NetworkNameCollection
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.42
|
# TS 51.011 Section 10.3.42
|
||||||
|
|
||||||
|
|
||||||
class EF_OPL(LinFixedEF):
|
class EF_OPL(LinFixedEF):
|
||||||
def __init__(self, fid='6fc6', sfid=None, name='EF.OPL', rec_len={8, 8}, desc='Operator PLMN List'):
|
def __init__(self, fid='6fc6', sfid=None, name='EF.OPL', rec_len={8, 8}, desc='Operator PLMN List'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
self._construct = Struct('lai'/Bytes(5), 'pnn_record_id'/Int8ub)
|
self._construct = Struct('lai'/Bytes(5), 'pnn_record_id'/Int8ub)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.44 + TS 31.102 4.2.62
|
# TS 51.011 Section 10.3.44 + TS 31.102 4.2.62
|
||||||
|
|
||||||
|
|
||||||
class EF_MBI(LinFixedEF):
|
class EF_MBI(LinFixedEF):
|
||||||
def __init__(self, fid='6fc9', sfid=None, name='EF.MBI', rec_len={4, 5}, desc='Mailbox Identifier'):
|
def __init__(self, fid='6fc9', sfid=None, name='EF.MBI', rec_len={4, 5}, desc='Mailbox Identifier'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
@@ -825,6 +938,8 @@ class EF_MBI(LinFixedEF):
|
|||||||
'mbi_other'/Int8ub, 'mbi_videocall'/COptional(Int8ub))
|
'mbi_other'/Int8ub, 'mbi_videocall'/COptional(Int8ub))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.45 + TS 31.102 4.2.63
|
# TS 51.011 Section 10.3.45 + TS 31.102 4.2.63
|
||||||
|
|
||||||
|
|
||||||
class EF_MWIS(LinFixedEF):
|
class EF_MWIS(LinFixedEF):
|
||||||
def __init__(self, fid='6fca', sfid=None, name='EF.MWIS', rec_len={5, 6},
|
def __init__(self, fid='6fca', sfid=None, name='EF.MWIS', rec_len={5, 6},
|
||||||
desc='Message Waiting Indication Status'):
|
desc='Message Waiting Indication Status'):
|
||||||
@@ -835,10 +950,13 @@ class EF_MWIS(LinFixedEF):
|
|||||||
'num_waiting_other'/Int8ub, 'num_waiting_videomail'/COptional(Int8ub))
|
'num_waiting_other'/Int8ub, 'num_waiting_videomail'/COptional(Int8ub))
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.66
|
# TS 51.011 Section 10.3.66
|
||||||
|
|
||||||
|
|
||||||
class EF_SPDI(TransparentEF):
|
class EF_SPDI(TransparentEF):
|
||||||
class ServiceProviderPLMN(BER_TLV_IE, tag=0x80):
|
class ServiceProviderPLMN(BER_TLV_IE, tag=0x80):
|
||||||
# flexible numbers of 3-byte PLMN records
|
# flexible numbers of 3-byte PLMN records
|
||||||
_construct = GreedyRange(BcdAdapter(Bytes(3)))
|
_construct = GreedyRange(BcdAdapter(Bytes(3)))
|
||||||
|
|
||||||
class SPDI(BER_TLV_IE, tag=0xA3, nested=[ServiceProviderPLMN]):
|
class SPDI(BER_TLV_IE, tag=0xA3, nested=[ServiceProviderPLMN]):
|
||||||
pass
|
pass
|
||||||
def __init__(self, fid='6fcd', sfid=None, name='EF.SPDI',
|
def __init__(self, fid='6fcd', sfid=None, name='EF.SPDI',
|
||||||
@@ -847,6 +965,8 @@ class EF_SPDI(TransparentEF):
|
|||||||
self._tlv = EF_SPDI.SPDI
|
self._tlv = EF_SPDI.SPDI
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.51
|
# TS 51.011 Section 10.3.51
|
||||||
|
|
||||||
|
|
||||||
class EF_MMSN(LinFixedEF):
|
class EF_MMSN(LinFixedEF):
|
||||||
def __init__(self, fid='6fce', sfid=None, name='EF.MMSN', rec_len={4, 20}, desc='MMS Notification'):
|
def __init__(self, fid='6fce', sfid=None, name='EF.MMSN', rec_len={4, 20}, desc='MMS Notification'):
|
||||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
|
||||||
@@ -854,20 +974,27 @@ class EF_MMSN(LinFixedEF):
|
|||||||
'mms_notification'/Bytes(this._.total_len-4), 'ext_record_nr'/Byte)
|
'mms_notification'/Bytes(this._.total_len-4), 'ext_record_nr'/Byte)
|
||||||
|
|
||||||
# TS 51.011 Annex K.1
|
# TS 51.011 Annex K.1
|
||||||
|
|
||||||
|
|
||||||
class MMS_Implementation(BER_TLV_IE, tag=0x80):
|
class MMS_Implementation(BER_TLV_IE, tag=0x80):
|
||||||
_construct = FlagsEnum(Byte, WAP=1)
|
_construct = FlagsEnum(Byte, WAP=1)
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.53
|
# TS 51.011 Section 10.3.53
|
||||||
|
|
||||||
|
|
||||||
class EF_MMSICP(TransparentEF):
|
class EF_MMSICP(TransparentEF):
|
||||||
class MMS_Relay_Server(BER_TLV_IE, tag=0x81):
|
class MMS_Relay_Server(BER_TLV_IE, tag=0x81):
|
||||||
# 3GPP TS 23.140
|
# 3GPP TS 23.140
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Interface_to_CN(BER_TLV_IE, tag=0x82):
|
class Interface_to_CN(BER_TLV_IE, tag=0x82):
|
||||||
# 3GPP TS 23.140
|
# 3GPP TS 23.140
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Gateway(BER_TLV_IE, tag=0x83):
|
class Gateway(BER_TLV_IE, tag=0x83):
|
||||||
# Address, Type of address, Port, Service, AuthType, AuthId, AuthPass / 3GPP TS 23.140
|
# Address, Type of address, Port, Service, AuthType, AuthId, AuthPass / 3GPP TS 23.140
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class MMS_ConnectivityParamters(TLV_IE_Collection,
|
class MMS_ConnectivityParamters(TLV_IE_Collection,
|
||||||
nested=[MMS_Implementation, MMS_Relay_Server, Interface_to_CN, Gateway]):
|
nested=[MMS_Implementation, MMS_Relay_Server, Interface_to_CN, Gateway]):
|
||||||
pass
|
pass
|
||||||
@@ -877,11 +1004,15 @@ class EF_MMSICP(TransparentEF):
|
|||||||
self._tlv = EF_MMSICP.MMS_ConnectivityParamters
|
self._tlv = EF_MMSICP.MMS_ConnectivityParamters
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.54
|
# TS 51.011 Section 10.3.54
|
||||||
|
|
||||||
|
|
||||||
class EF_MMSUP(LinFixedEF):
|
class EF_MMSUP(LinFixedEF):
|
||||||
class MMS_UserPref_ProfileName(BER_TLV_IE, tag=0x81):
|
class MMS_UserPref_ProfileName(BER_TLV_IE, tag=0x81):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class MMS_UserPref_Info(BER_TLV_IE, tag=0x82):
|
class MMS_UserPref_Info(BER_TLV_IE, tag=0x82):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class MMS_User_Preferences(TLV_IE_Collection,
|
class MMS_User_Preferences(TLV_IE_Collection,
|
||||||
nested=[MMS_Implementation, MMS_UserPref_ProfileName, MMS_UserPref_Info]):
|
nested=[MMS_Implementation, MMS_UserPref_ProfileName, MMS_UserPref_Info]):
|
||||||
pass
|
pass
|
||||||
@@ -891,6 +1022,8 @@ class EF_MMSUP(LinFixedEF):
|
|||||||
self._tlv = EF_MMSUP.MMS_User_Preferences
|
self._tlv = EF_MMSUP.MMS_User_Preferences
|
||||||
|
|
||||||
# TS 51.011 Section 10.3.55
|
# TS 51.011 Section 10.3.55
|
||||||
|
|
||||||
|
|
||||||
class EF_MMSUCP(TransparentEF):
|
class EF_MMSUCP(TransparentEF):
|
||||||
def __init__(self, fid='6fd2', sfid=None, name='EF.MMSUCP', size={1, None},
|
def __init__(self, fid='6fd2', sfid=None, name='EF.MMSUCP', size={1, None},
|
||||||
desc='MMS User Connectivity Parameters'):
|
desc='MMS User Connectivity Parameters'):
|
||||||
@@ -905,25 +1038,33 @@ class DF_GSM(CardDF):
|
|||||||
EF_IMSI(),
|
EF_IMSI(),
|
||||||
EF_Kc(),
|
EF_Kc(),
|
||||||
EF_PLMNsel(),
|
EF_PLMNsel(),
|
||||||
TransparentEF('6f31', None, 'EF.HPPLMN', 'Higher Priority PLMN search period'),
|
TransparentEF('6f31', None, 'EF.HPPLMN',
|
||||||
|
'Higher Priority PLMN search period'),
|
||||||
EF_ACMmax(),
|
EF_ACMmax(),
|
||||||
EF_ServiceTable('6f38', None, 'EF.SST', 'SIM service table', table=EF_SST_map, size={2,16}),
|
EF_ServiceTable('6f38', None, 'EF.SST',
|
||||||
CyclicEF('6f39', None, 'EF.ACM', 'Accumulated call meter', rec_len={3,3}),
|
'SIM service table', table=EF_SST_map, size={2, 16}),
|
||||||
|
CyclicEF('6f39', None, 'EF.ACM',
|
||||||
|
'Accumulated call meter', rec_len={3, 3}),
|
||||||
TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),
|
TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),
|
||||||
TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),
|
TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),
|
||||||
EF_SPN(),
|
EF_SPN(),
|
||||||
TransparentEF('6f41', None, 'EF.PUCT', 'Price per unit and currency table', size={5,5}),
|
TransparentEF('6f41', None, 'EF.PUCT',
|
||||||
|
'Price per unit and currency table', size={5, 5}),
|
||||||
EF_CBMI(),
|
EF_CBMI(),
|
||||||
TransparentEF('6f7f', None, 'EF.BCCH', 'Broadcast control channels', size={16,16}),
|
TransparentEF('6f7f', None, 'EF.BCCH',
|
||||||
|
'Broadcast control channels', size={16, 16}),
|
||||||
EF_ACC(),
|
EF_ACC(),
|
||||||
EF_PLMNsel('6f7b', None, 'EF.FPLMN', 'Forbidden PLMNs', size={12,12}),
|
EF_PLMNsel('6f7b', None, 'EF.FPLMN',
|
||||||
|
'Forbidden PLMNs', size={12, 12}),
|
||||||
EF_LOCI(),
|
EF_LOCI(),
|
||||||
EF_AD(),
|
EF_AD(),
|
||||||
TransparentEF('6fa3', None, 'EF.Phase', 'Phase identification', size={1,1}),
|
TransparentEF('6fa3', None, 'EF.Phase',
|
||||||
|
'Phase identification', size={1, 1}),
|
||||||
EF_VGCS(),
|
EF_VGCS(),
|
||||||
EF_VGCSS(),
|
EF_VGCSS(),
|
||||||
EF_VGCS('6fb3', None, 'EF.VBS', 'Voice Broadcast Service'),
|
EF_VGCS('6fb3', None, 'EF.VBS', 'Voice Broadcast Service'),
|
||||||
EF_VGCSS('6fb4', None, 'EF.VBSS', 'Voice Broadcast Service Status'),
|
EF_VGCSS('6fb4', None, 'EF.VBSS',
|
||||||
|
'Voice Broadcast Service Status'),
|
||||||
EF_eMLPP(),
|
EF_eMLPP(),
|
||||||
EF_AAeM(),
|
EF_AAeM(),
|
||||||
EF_CBMID(),
|
EF_CBMID(),
|
||||||
@@ -939,7 +1080,8 @@ class DF_GSM(CardDF):
|
|||||||
'User controlled PLMN Selector with Access Technology'),
|
'User controlled PLMN Selector with Access Technology'),
|
||||||
EF_xPLMNwAcT('6f61', None, 'EF.OPLMNwAcT',
|
EF_xPLMNwAcT('6f61', None, 'EF.OPLMNwAcT',
|
||||||
'Operator controlled PLMN Selector with Access Technology'),
|
'Operator controlled PLMN Selector with Access Technology'),
|
||||||
EF_xPLMNwAcT('6f62', None, 'EF.HPLMNwAcT', 'HPLMN Selector with Access Technology'),
|
EF_xPLMNwAcT('6f62', None, 'EF.HPLMNwAcT',
|
||||||
|
'HPLMN Selector with Access Technology'),
|
||||||
EF_CPBCCH(),
|
EF_CPBCCH(),
|
||||||
EF_InvScan(),
|
EF_InvScan(),
|
||||||
EF_PNN(),
|
EF_PNN(),
|
||||||
@@ -947,7 +1089,8 @@ class DF_GSM(CardDF):
|
|||||||
EF_ADN('6fc7', None, 'EF.MBDN', 'Mailbox Dialling Numbers'),
|
EF_ADN('6fc7', None, 'EF.MBDN', 'Mailbox Dialling Numbers'),
|
||||||
EF_MBI(),
|
EF_MBI(),
|
||||||
EF_MWIS(),
|
EF_MWIS(),
|
||||||
EF_ADN('6fcb', None, 'EF.CFIS', 'Call Forwarding Indication Status'),
|
EF_ADN('6fcb', None, 'EF.CFIS',
|
||||||
|
'Call Forwarding Indication Status'),
|
||||||
EF_EXT('6fc8', None, 'EF.EXT6', 'Externsion6 (MBDN)'),
|
EF_EXT('6fc8', None, 'EF.EXT6', 'Externsion6 (MBDN)'),
|
||||||
EF_EXT('6fcc', None, 'EF.EXT7', 'Externsion7 (CFIS)'),
|
EF_EXT('6fcc', None, 'EF.EXT7', 'Externsion7 (CFIS)'),
|
||||||
EF_SPDI(),
|
EF_SPDI(),
|
||||||
@@ -959,6 +1102,7 @@ class DF_GSM(CardDF):
|
|||||||
]
|
]
|
||||||
self.add_files(files)
|
self.add_files(files)
|
||||||
|
|
||||||
|
|
||||||
class CardProfileSIM(CardProfile):
|
class CardProfileSIM(CardProfile):
|
||||||
|
|
||||||
ORDER = 2
|
ORDER = 2
|
||||||
@@ -1001,7 +1145,8 @@ class CardProfileSIM(CardProfile):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
super().__init__('SIM', desc='GSM SIM Card', cla="a0", sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw)
|
super().__init__('SIM', desc='GSM SIM Card', cla="a0",
|
||||||
|
sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode_select_response(resp_hex: str) -> object:
|
def decode_select_response(resp_hex: str) -> object:
|
||||||
@@ -1021,8 +1166,10 @@ class CardProfileSIM(CardProfile):
|
|||||||
'proprietary_info': {},
|
'proprietary_info': {},
|
||||||
}
|
}
|
||||||
ret['file_id'] = b2h(resp_bin[4:6])
|
ret['file_id'] = b2h(resp_bin[4:6])
|
||||||
ret['proprietary_info']['available_memory'] = int.from_bytes(resp_bin[2:4], 'big')
|
ret['proprietary_info']['available_memory'] = int.from_bytes(
|
||||||
file_type = type_of_file_map[resp_bin[6]] if resp_bin[6] in type_of_file_map else resp_bin[6]
|
resp_bin[2:4], 'big')
|
||||||
|
file_type = type_of_file_map[resp_bin[6]
|
||||||
|
] if resp_bin[6] in type_of_file_map else resp_bin[6]
|
||||||
ret['file_descriptor']['file_type'] = file_type
|
ret['file_descriptor']['file_type'] = file_type
|
||||||
if file_type in ['mf', 'df']:
|
if file_type in ['mf', 'df']:
|
||||||
ret['file_characteristics'] = b2h(resp_bin[13:14])
|
ret['file_characteristics'] = b2h(resp_bin[13:14])
|
||||||
@@ -1031,7 +1178,8 @@ class CardProfileSIM(CardProfile):
|
|||||||
ret['num_chv_unblock_adm_codes'] = int(resp_bin[16])
|
ret['num_chv_unblock_adm_codes'] = int(resp_bin[16])
|
||||||
# CHV / UNBLOCK CHV stats
|
# CHV / UNBLOCK CHV stats
|
||||||
elif file_type in ['working_ef']:
|
elif file_type in ['working_ef']:
|
||||||
file_struct = struct_of_file_map[resp_bin[13]] if resp_bin[13] in struct_of_file_map else resp_bin[13]
|
file_struct = struct_of_file_map[resp_bin[13]
|
||||||
|
] if resp_bin[13] in struct_of_file_map else resp_bin[13]
|
||||||
ret['file_descriptor']['structure'] = file_struct
|
ret['file_descriptor']['structure'] = file_struct
|
||||||
ret['access_conditions'] = b2h(resp_bin[8:10])
|
ret['access_conditions'] = b2h(resp_bin[8:10])
|
||||||
if resp_bin[11] & 0x01 == 0:
|
if resp_bin[11] & 0x01 == 0:
|
||||||
|
|||||||
142
pySim/utils.py
142
pySim/utils.py
@@ -29,41 +29,50 @@ from typing import Optional, List, Dict, Any, Tuple
|
|||||||
# just to differentiate strings of hex nibbles from everything else
|
# just to differentiate strings of hex nibbles from everything else
|
||||||
Hexstr = str
|
Hexstr = str
|
||||||
|
|
||||||
|
|
||||||
def h2b(s: Hexstr) -> bytearray:
|
def h2b(s: Hexstr) -> bytearray:
|
||||||
"""convert from a string of hex nibbles to a sequence of bytes"""
|
"""convert from a string of hex nibbles to a sequence of bytes"""
|
||||||
return bytearray.fromhex(s)
|
return bytearray.fromhex(s)
|
||||||
|
|
||||||
|
|
||||||
def b2h(b: bytearray) -> Hexstr:
|
def b2h(b: bytearray) -> Hexstr:
|
||||||
"""convert from a sequence of bytes to a string of hex nibbles"""
|
"""convert from a sequence of bytes to a string of hex nibbles"""
|
||||||
return ''.join(['%02x' % (x) for x in b])
|
return ''.join(['%02x' % (x) for x in b])
|
||||||
|
|
||||||
|
|
||||||
def h2i(s: Hexstr) -> List[int]:
|
def h2i(s: Hexstr) -> List[int]:
|
||||||
"""convert from a string of hex nibbles to a list of integers"""
|
"""convert from a string of hex nibbles to a list of integers"""
|
||||||
return [(int(x, 16) << 4)+int(y, 16) for x, y in zip(s[0::2], s[1::2])]
|
return [(int(x, 16) << 4)+int(y, 16) for x, y in zip(s[0::2], s[1::2])]
|
||||||
|
|
||||||
|
|
||||||
def i2h(s: List[int]) -> Hexstr:
|
def i2h(s: List[int]) -> Hexstr:
|
||||||
"""convert from a list of integers to a string of hex nibbles"""
|
"""convert from a list of integers to a string of hex nibbles"""
|
||||||
return ''.join(['%02x' % (x) for x in s])
|
return ''.join(['%02x' % (x) for x in s])
|
||||||
|
|
||||||
|
|
||||||
def h2s(s: Hexstr) -> str:
|
def h2s(s: Hexstr) -> str:
|
||||||
"""convert from a string of hex nibbles to an ASCII string"""
|
"""convert from a string of hex nibbles to an ASCII string"""
|
||||||
return ''.join([chr((int(x, 16) << 4)+int(y, 16)) for x, y in zip(s[0::2], s[1::2])
|
return ''.join([chr((int(x, 16) << 4)+int(y, 16)) for x, y in zip(s[0::2], s[1::2])
|
||||||
if int(x + y, 16) != 0xff])
|
if int(x + y, 16) != 0xff])
|
||||||
|
|
||||||
|
|
||||||
def s2h(s: str) -> Hexstr:
|
def s2h(s: str) -> Hexstr:
|
||||||
"""convert from an ASCII string to a string of hex nibbles"""
|
"""convert from an ASCII string to a string of hex nibbles"""
|
||||||
b = bytearray()
|
b = bytearray()
|
||||||
b.extend(map(ord, s))
|
b.extend(map(ord, s))
|
||||||
return b2h(b)
|
return b2h(b)
|
||||||
|
|
||||||
|
|
||||||
def i2s(s: List[int]) -> str:
|
def i2s(s: List[int]) -> str:
|
||||||
"""convert from a list of integers to an ASCII string"""
|
"""convert from a list of integers to an ASCII string"""
|
||||||
return ''.join([chr(x) for x in s])
|
return ''.join([chr(x) for x in s])
|
||||||
|
|
||||||
|
|
||||||
def swap_nibbles(s: Hexstr) -> Hexstr:
|
def swap_nibbles(s: Hexstr) -> Hexstr:
|
||||||
"""swap the nibbles in a hex string"""
|
"""swap the nibbles in a hex string"""
|
||||||
return ''.join([x+y for x, y in zip(s[1::2], s[0::2])])
|
return ''.join([x+y for x, y in zip(s[1::2], s[0::2])])
|
||||||
|
|
||||||
|
|
||||||
def rpad(s: str, l: int, c='f') -> str:
|
def rpad(s: str, l: int, c='f') -> str:
|
||||||
"""pad string on the right side.
|
"""pad string on the right side.
|
||||||
Args:
|
Args:
|
||||||
@@ -75,6 +84,7 @@ def rpad(s:str, l:int, c='f') -> str:
|
|||||||
"""
|
"""
|
||||||
return s + c * (l - len(s))
|
return s + c * (l - len(s))
|
||||||
|
|
||||||
|
|
||||||
def lpad(s: str, l: int, c='f') -> str:
|
def lpad(s: str, l: int, c='f') -> str:
|
||||||
"""pad string on the left side.
|
"""pad string on the left side.
|
||||||
Args:
|
Args:
|
||||||
@@ -86,9 +96,11 @@ def lpad(s:str, l:int, c='f') -> str:
|
|||||||
"""
|
"""
|
||||||
return c * (l - len(s)) + s
|
return c * (l - len(s)) + s
|
||||||
|
|
||||||
|
|
||||||
def half_round_up(n: int) -> int:
|
def half_round_up(n: int) -> int:
|
||||||
return (n + 1)//2
|
return (n + 1)//2
|
||||||
|
|
||||||
|
|
||||||
def str_sanitize(s: str) -> str:
|
def str_sanitize(s: str) -> str:
|
||||||
"""replace all non printable chars, line breaks and whitespaces, with ' ', make sure that
|
"""replace all non printable chars, line breaks and whitespaces, with ' ', make sure that
|
||||||
there are no whitespaces at the end and at the beginning of the string.
|
there are no whitespaces at the end and at the beginning of the string.
|
||||||
@@ -107,10 +119,12 @@ def str_sanitize(s:str) -> str:
|
|||||||
# poor man's COMPREHENSION-TLV decoder.
|
# poor man's COMPREHENSION-TLV decoder.
|
||||||
#########################################################################
|
#########################################################################
|
||||||
|
|
||||||
|
|
||||||
def comprehensiontlv_parse_tag_raw(binary: bytes) -> Tuple[int, bytes]:
|
def comprehensiontlv_parse_tag_raw(binary: bytes) -> Tuple[int, bytes]:
|
||||||
"""Parse a single Tag according to ETSI TS 101 220 Section 7.1.1"""
|
"""Parse a single Tag according to ETSI TS 101 220 Section 7.1.1"""
|
||||||
if binary[0] in [0x00, 0x80, 0xff]:
|
if binary[0] in [0x00, 0x80, 0xff]:
|
||||||
raise ValueError("Found illegal value 0x%02x in %s" % (binary[0], binary))
|
raise ValueError("Found illegal value 0x%02x in %s" %
|
||||||
|
(binary[0], binary))
|
||||||
if binary[0] == 0x7f:
|
if binary[0] == 0x7f:
|
||||||
# three-byte tag
|
# three-byte tag
|
||||||
tag = binary[0] << 16 | binary[1] << 8 | binary[2]
|
tag = binary[0] << 16 | binary[1] << 8 | binary[2]
|
||||||
@@ -122,10 +136,12 @@ def comprehensiontlv_parse_tag_raw(binary:bytes) -> Tuple[int, bytes]:
|
|||||||
tag = binary[0]
|
tag = binary[0]
|
||||||
return (tag, binary[1:])
|
return (tag, binary[1:])
|
||||||
|
|
||||||
|
|
||||||
def comprehensiontlv_parse_tag(binary: bytes) -> Tuple[dict, bytes]:
|
def comprehensiontlv_parse_tag(binary: bytes) -> Tuple[dict, bytes]:
|
||||||
"""Parse a single Tag according to ETSI TS 101 220 Section 7.1.1"""
|
"""Parse a single Tag according to ETSI TS 101 220 Section 7.1.1"""
|
||||||
if binary[0] in [0x00, 0x80, 0xff]:
|
if binary[0] in [0x00, 0x80, 0xff]:
|
||||||
raise ValueError("Found illegal value 0x%02x in %s" % (binary[0], binary))
|
raise ValueError("Found illegal value 0x%02x in %s" %
|
||||||
|
(binary[0], binary))
|
||||||
if binary[0] == 0x7f:
|
if binary[0] == 0x7f:
|
||||||
# three-byte tag
|
# three-byte tag
|
||||||
tag = (binary[1] & 0x7f) << 8
|
tag = (binary[1] & 0x7f) << 8
|
||||||
@@ -138,6 +154,7 @@ def comprehensiontlv_parse_tag(binary:bytes) -> Tuple[dict, bytes]:
|
|||||||
compr = True if binary[0] & 0x80 else False
|
compr = True if binary[0] & 0x80 else False
|
||||||
return ({'comprehension': compr, 'tag': tag}, binary[1:])
|
return ({'comprehension': compr, 'tag': tag}, binary[1:])
|
||||||
|
|
||||||
|
|
||||||
def comprehensiontlv_encode_tag(tag) -> bytes:
|
def comprehensiontlv_encode_tag(tag) -> bytes:
|
||||||
"""Encode a single Tag according to ETSI TS 101 220 Section 7.1.1"""
|
"""Encode a single Tag according to ETSI TS 101 220 Section 7.1.1"""
|
||||||
# permit caller to specify tag also as integer value
|
# permit caller to specify tag also as integer value
|
||||||
@@ -161,6 +178,7 @@ def comprehensiontlv_encode_tag(tag) -> bytes:
|
|||||||
|
|
||||||
# length value coding is equal to BER-TLV
|
# length value coding is equal to BER-TLV
|
||||||
|
|
||||||
|
|
||||||
def comprehensiontlv_parse_one(binary: bytes) -> Tuple[dict, int, bytes, bytes]:
|
def comprehensiontlv_parse_one(binary: bytes) -> Tuple[dict, int, bytes, bytes]:
|
||||||
"""Parse a single TLV IE at the start of the given binary data.
|
"""Parse a single TLV IE at the start of the given binary data.
|
||||||
Args:
|
Args:
|
||||||
@@ -175,7 +193,6 @@ def comprehensiontlv_parse_one(binary:bytes) -> Tuple[dict, int, bytes, bytes]:
|
|||||||
return (tagdict, length, value, remainder)
|
return (tagdict, length, value, remainder)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
# poor man's BER-TLV decoder. To be a more sophisticated OO library later
|
# poor man's BER-TLV decoder. To be a more sophisticated OO library later
|
||||||
#########################################################################
|
#########################################################################
|
||||||
@@ -204,6 +221,7 @@ def bertlv_parse_tag_raw(binary:bytes) -> Tuple[int, bytes]:
|
|||||||
i += 1
|
i += 1
|
||||||
return tag, binary[i:]
|
return tag, binary[i:]
|
||||||
|
|
||||||
|
|
||||||
def bertlv_parse_tag(binary: bytes) -> Tuple[dict, bytes]:
|
def bertlv_parse_tag(binary: bytes) -> Tuple[dict, bytes]:
|
||||||
"""Parse a single Tag value according to ITU-T X.690 8.1.2
|
"""Parse a single Tag value according to ITU-T X.690 8.1.2
|
||||||
Args:
|
Args:
|
||||||
@@ -227,6 +245,7 @@ def bertlv_parse_tag(binary:bytes) -> Tuple[dict, bytes]:
|
|||||||
i += 1
|
i += 1
|
||||||
return ({'class': cls, 'constructed': constructed, 'tag': tag}, binary[i:])
|
return ({'class': cls, 'constructed': constructed, 'tag': tag}, binary[i:])
|
||||||
|
|
||||||
|
|
||||||
def bertlv_encode_tag(t) -> bytes:
|
def bertlv_encode_tag(t) -> bytes:
|
||||||
"""Encode a single Tag value according to ITU-T X.690 8.1.2
|
"""Encode a single Tag value according to ITU-T X.690 8.1.2
|
||||||
"""
|
"""
|
||||||
@@ -272,6 +291,7 @@ def bertlv_encode_tag(t) -> bytes:
|
|||||||
break
|
break
|
||||||
return tag_bytes
|
return tag_bytes
|
||||||
|
|
||||||
|
|
||||||
def bertlv_parse_len(binary: bytes) -> Tuple[int, bytes]:
|
def bertlv_parse_len(binary: bytes) -> Tuple[int, bytes]:
|
||||||
"""Parse a single Length value according to ITU-T X.690 8.1.3;
|
"""Parse a single Length value according to ITU-T X.690 8.1.3;
|
||||||
only the definite form is supported here.
|
only the definite form is supported here.
|
||||||
@@ -290,6 +310,7 @@ def bertlv_parse_len(binary:bytes) -> Tuple[int, bytes]:
|
|||||||
length |= binary[i]
|
length |= binary[i]
|
||||||
return (length, binary[1+num_len_oct:])
|
return (length, binary[1+num_len_oct:])
|
||||||
|
|
||||||
|
|
||||||
def bertlv_encode_len(length: int) -> bytes:
|
def bertlv_encode_len(length: int) -> bytes:
|
||||||
"""Encode a single Length value according to ITU-T X.690 8.1.3;
|
"""Encode a single Length value according to ITU-T X.690 8.1.3;
|
||||||
only the definite form is supported here.
|
only the definite form is supported here.
|
||||||
@@ -311,6 +332,7 @@ def bertlv_encode_len(length:int) -> bytes:
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Length > 32bits not supported")
|
raise ValueError("Length > 32bits not supported")
|
||||||
|
|
||||||
|
|
||||||
def bertlv_parse_one(binary: bytes) -> Tuple[dict, int, bytes, bytes]:
|
def bertlv_parse_one(binary: bytes) -> Tuple[dict, int, bytes, bytes]:
|
||||||
"""Parse a single TLV IE at the start of the given binary data.
|
"""Parse a single TLV IE at the start of the given binary data.
|
||||||
Args:
|
Args:
|
||||||
@@ -325,7 +347,6 @@ def bertlv_parse_one(binary:bytes) -> Tuple[dict, int, bytes, bytes]:
|
|||||||
return (tagdict, length, value, remainder)
|
return (tagdict, length, value, remainder)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# IMSI encoded format:
|
# IMSI encoded format:
|
||||||
# For IMSI 0123456789ABCDE:
|
# For IMSI 0123456789ABCDE:
|
||||||
#
|
#
|
||||||
@@ -343,11 +364,13 @@ def bertlv_parse_one(binary:bytes) -> Tuple[dict, int, bytes, bytes]:
|
|||||||
|
|
||||||
def enc_imsi(imsi: str):
|
def enc_imsi(imsi: str):
|
||||||
"""Converts a string IMSI into the encoded value of the EF"""
|
"""Converts a string IMSI into the encoded value of the EF"""
|
||||||
l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
|
l = half_round_up(
|
||||||
|
len(imsi) + 1) # Required bytes - include space for odd/even indicator
|
||||||
oe = len(imsi) & 1 # Odd (1) / Even (0)
|
oe = len(imsi) & 1 # Odd (1) / Even (0)
|
||||||
ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe << 3) | 1, rpad(imsi, 15)))
|
ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe << 3) | 1, rpad(imsi, 15)))
|
||||||
return ei
|
return ei
|
||||||
|
|
||||||
|
|
||||||
def dec_imsi(ef: Hexstr) -> Optional[str]:
|
def dec_imsi(ef: Hexstr) -> Optional[str]:
|
||||||
"""Converts an EF value to the IMSI string representation"""
|
"""Converts an EF value to the IMSI string representation"""
|
||||||
if len(ef) < 4:
|
if len(ef) < 4:
|
||||||
@@ -366,12 +389,15 @@ def dec_imsi(ef:Hexstr) -> Optional[str]:
|
|||||||
imsi = swapped[1:]
|
imsi = swapped[1:]
|
||||||
return imsi
|
return imsi
|
||||||
|
|
||||||
|
|
||||||
def dec_iccid(ef: Hexstr) -> str:
|
def dec_iccid(ef: Hexstr) -> str:
|
||||||
return swap_nibbles(ef).strip('f')
|
return swap_nibbles(ef).strip('f')
|
||||||
|
|
||||||
|
|
||||||
def enc_iccid(iccid: str) -> Hexstr:
|
def enc_iccid(iccid: str) -> Hexstr:
|
||||||
return swap_nibbles(rpad(iccid, 20))
|
return swap_nibbles(rpad(iccid, 20))
|
||||||
|
|
||||||
|
|
||||||
def enc_plmn(mcc: Hexstr, mnc: Hexstr) -> Hexstr:
|
def enc_plmn(mcc: Hexstr, mnc: Hexstr) -> Hexstr:
|
||||||
"""Converts integer MCC/MNC into 3 bytes for EF"""
|
"""Converts integer MCC/MNC into 3 bytes for EF"""
|
||||||
|
|
||||||
@@ -398,6 +424,7 @@ def enc_plmn(mcc:Hexstr, mnc:Hexstr) -> Hexstr:
|
|||||||
|
|
||||||
return (mcc[1] + mcc[0]) + (mnc[2] + mcc[2]) + (mnc[1] + mnc[0])
|
return (mcc[1] + mcc[0]) + (mnc[2] + mcc[2]) + (mnc[1] + mnc[0])
|
||||||
|
|
||||||
|
|
||||||
def dec_plmn(threehexbytes: Hexstr) -> dict:
|
def dec_plmn(threehexbytes: Hexstr) -> dict:
|
||||||
res = {'mcc': "0", 'mnc': "0"}
|
res = {'mcc': "0", 'mnc': "0"}
|
||||||
dec_mcc_from_plmn_str(threehexbytes)
|
dec_mcc_from_plmn_str(threehexbytes)
|
||||||
@@ -405,6 +432,7 @@ def dec_plmn(threehexbytes:Hexstr) -> dict:
|
|||||||
res['mnc'] = dec_mnc_from_plmn_str(threehexbytes)
|
res['mnc'] = dec_mnc_from_plmn_str(threehexbytes)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def dec_spn(ef):
|
def dec_spn(ef):
|
||||||
"""Obsolete, kept for API compatibility"""
|
"""Obsolete, kept for API compatibility"""
|
||||||
from ts_51_011 import EF_SPN
|
from ts_51_011 import EF_SPN
|
||||||
@@ -414,6 +442,7 @@ def dec_spn(ef):
|
|||||||
name = abstract_data['spn']
|
name = abstract_data['spn']
|
||||||
return (name, show_in_hplmn, hide_in_oplmn)
|
return (name, show_in_hplmn, hide_in_oplmn)
|
||||||
|
|
||||||
|
|
||||||
def enc_spn(name: str, show_in_hplmn=False, hide_in_oplmn=False):
|
def enc_spn(name: str, show_in_hplmn=False, hide_in_oplmn=False):
|
||||||
"""Obsolete, kept for API compatibility"""
|
"""Obsolete, kept for API compatibility"""
|
||||||
from ts_51_011 import EF_SPN
|
from ts_51_011 import EF_SPN
|
||||||
@@ -424,10 +453,13 @@ def enc_spn(name:str, show_in_hplmn=False, hide_in_oplmn=False):
|
|||||||
}
|
}
|
||||||
return EF_SPN().encode_hex(abstract_data)
|
return EF_SPN().encode_hex(abstract_data)
|
||||||
|
|
||||||
|
|
||||||
def hexstr_to_Nbytearr(s, nbytes):
|
def hexstr_to_Nbytearr(s, nbytes):
|
||||||
return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2))]
|
return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2))]
|
||||||
|
|
||||||
# Accepts hex string representing three bytes
|
# Accepts hex string representing three bytes
|
||||||
|
|
||||||
|
|
||||||
def dec_mcc_from_plmn(plmn: Hexstr) -> int:
|
def dec_mcc_from_plmn(plmn: Hexstr) -> int:
|
||||||
ia = h2i(plmn)
|
ia = h2i(plmn)
|
||||||
digit1 = ia[0] & 0x0F # 1st byte, LSB
|
digit1 = ia[0] & 0x0F # 1st byte, LSB
|
||||||
@@ -437,6 +469,7 @@ def dec_mcc_from_plmn(plmn:Hexstr) -> int:
|
|||||||
return 0xFFF # 4095
|
return 0xFFF # 4095
|
||||||
return derive_mcc(digit1, digit2, digit3)
|
return derive_mcc(digit1, digit2, digit3)
|
||||||
|
|
||||||
|
|
||||||
def dec_mcc_from_plmn_str(plmn: Hexstr) -> str:
|
def dec_mcc_from_plmn_str(plmn: Hexstr) -> str:
|
||||||
digit1 = plmn[1] # 1st byte, LSB
|
digit1 = plmn[1] # 1st byte, LSB
|
||||||
digit2 = plmn[0] # 1st byte, MSB
|
digit2 = plmn[0] # 1st byte, MSB
|
||||||
@@ -444,6 +477,7 @@ def dec_mcc_from_plmn_str(plmn:Hexstr) -> str:
|
|||||||
res = digit1 + digit2 + digit3
|
res = digit1 + digit2 + digit3
|
||||||
return res.upper().strip("F")
|
return res.upper().strip("F")
|
||||||
|
|
||||||
|
|
||||||
def dec_mnc_from_plmn(plmn: Hexstr) -> int:
|
def dec_mnc_from_plmn(plmn: Hexstr) -> int:
|
||||||
ia = h2i(plmn)
|
ia = h2i(plmn)
|
||||||
digit1 = ia[2] & 0x0F # 3rd byte, LSB
|
digit1 = ia[2] & 0x0F # 3rd byte, LSB
|
||||||
@@ -453,6 +487,7 @@ def dec_mnc_from_plmn(plmn:Hexstr) -> int:
|
|||||||
return 0xFFF # 4095
|
return 0xFFF # 4095
|
||||||
return derive_mnc(digit1, digit2, digit3)
|
return derive_mnc(digit1, digit2, digit3)
|
||||||
|
|
||||||
|
|
||||||
def dec_mnc_from_plmn_str(plmn: Hexstr) -> str:
|
def dec_mnc_from_plmn_str(plmn: Hexstr) -> str:
|
||||||
digit1 = plmn[5] # 3rd byte, LSB
|
digit1 = plmn[5] # 3rd byte, LSB
|
||||||
digit2 = plmn[4] # 3rd byte, MSB
|
digit2 = plmn[4] # 3rd byte, MSB
|
||||||
@@ -460,6 +495,7 @@ def dec_mnc_from_plmn_str(plmn:Hexstr) -> str:
|
|||||||
res = digit1 + digit2 + digit3
|
res = digit1 + digit2 + digit3
|
||||||
return res.upper().strip("F")
|
return res.upper().strip("F")
|
||||||
|
|
||||||
|
|
||||||
def dec_act(twohexbytes: Hexstr) -> List[str]:
|
def dec_act(twohexbytes: Hexstr) -> List[str]:
|
||||||
act_list = [
|
act_list = [
|
||||||
{'bit': 15, 'name': "UTRAN"},
|
{'bit': 15, 'name': "UTRAN"},
|
||||||
@@ -491,17 +527,21 @@ def dec_act(twohexbytes:Hexstr) -> List[str]:
|
|||||||
sel.append(a['name'])
|
sel.append(a['name'])
|
||||||
return sel
|
return sel
|
||||||
|
|
||||||
|
|
||||||
def dec_xplmn_w_act(fivehexbytes: Hexstr) -> Dict[str, Any]:
|
def dec_xplmn_w_act(fivehexbytes: Hexstr) -> Dict[str, Any]:
|
||||||
res = {'mcc': "0", 'mnc': "0", 'act': []}
|
res = {'mcc': "0", 'mnc': "0", 'act': []}
|
||||||
plmn_chars = 6
|
plmn_chars = 6
|
||||||
act_chars = 4
|
act_chars = 4
|
||||||
plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
|
# first three bytes (six ascii hex chars)
|
||||||
act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
|
plmn_str = fivehexbytes[:plmn_chars]
|
||||||
|
# two bytes after first three bytes
|
||||||
|
act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars]
|
||||||
res['mcc'] = dec_mcc_from_plmn_str(plmn_str)
|
res['mcc'] = dec_mcc_from_plmn_str(plmn_str)
|
||||||
res['mnc'] = dec_mnc_from_plmn_str(plmn_str)
|
res['mnc'] = dec_mnc_from_plmn_str(plmn_str)
|
||||||
res['act'] = dec_act(act_str)
|
res['act'] = dec_act(act_str)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def format_xplmn_w_act(hexstr):
|
def format_xplmn_w_act(hexstr):
|
||||||
s = ""
|
s = ""
|
||||||
for rec_data in hexstr_to_Nbytearr(hexstr, 5):
|
for rec_data in hexstr_to_Nbytearr(hexstr, 5):
|
||||||
@@ -509,10 +549,12 @@ def format_xplmn_w_act(hexstr):
|
|||||||
if rec_info['mcc'] == "" and rec_info['mnc'] == "":
|
if rec_info['mcc'] == "" and rec_info['mnc'] == "":
|
||||||
rec_str = "unused"
|
rec_str = "unused"
|
||||||
else:
|
else:
|
||||||
rec_str = "MCC: %s MNC: %s AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
|
rec_str = "MCC: %s MNC: %s AcT: %s" % (
|
||||||
|
rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
|
||||||
s += "\t%s # %s\n" % (rec_data, rec_str)
|
s += "\t%s # %s\n" % (rec_data, rec_str)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def dec_loci(hexstr):
|
def dec_loci(hexstr):
|
||||||
res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
|
res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
|
||||||
res['tmsi'] = hexstr[:8]
|
res['tmsi'] = hexstr[:8]
|
||||||
@@ -522,8 +564,10 @@ def dec_loci(hexstr):
|
|||||||
res['status'] = h2i(hexstr[20:22])
|
res['status'] = h2i(hexstr[20:22])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def dec_psloci(hexstr):
|
def dec_psloci(hexstr):
|
||||||
res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
|
res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0,
|
||||||
|
'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
|
||||||
res['p-tmsi'] = hexstr[:8]
|
res['p-tmsi'] = hexstr[:8]
|
||||||
res['p-tmsi-sig'] = hexstr[8:14]
|
res['p-tmsi-sig'] = hexstr[8:14]
|
||||||
res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
|
res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
|
||||||
@@ -533,6 +577,7 @@ def dec_psloci(hexstr):
|
|||||||
res['status'] = h2i(hexstr[26:28])
|
res['status'] = h2i(hexstr[26:28])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def dec_epsloci(hexstr):
|
def dec_epsloci(hexstr):
|
||||||
res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
|
res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
|
||||||
res['guti'] = hexstr[:24]
|
res['guti'] = hexstr[:24]
|
||||||
@@ -543,14 +588,17 @@ def dec_epsloci(hexstr):
|
|||||||
res['status'] = h2i(hexstr[34:36])
|
res['status'] = h2i(hexstr[34:36])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def dec_xplmn(threehexbytes: Hexstr) -> dict:
|
def dec_xplmn(threehexbytes: Hexstr) -> dict:
|
||||||
res = {'mcc': 0, 'mnc': 0, 'act': []}
|
res = {'mcc': 0, 'mnc': 0, 'act': []}
|
||||||
plmn_chars = 6
|
plmn_chars = 6
|
||||||
plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
|
# first three bytes (six ascii hex chars)
|
||||||
|
plmn_str = threehexbytes[:plmn_chars]
|
||||||
res['mcc'] = dec_mcc_from_plmn(plmn_str)
|
res['mcc'] = dec_mcc_from_plmn(plmn_str)
|
||||||
res['mnc'] = dec_mnc_from_plmn(plmn_str)
|
res['mnc'] = dec_mnc_from_plmn(plmn_str)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def format_xplmn(hexstr: Hexstr) -> str:
|
def format_xplmn(hexstr: Hexstr) -> str:
|
||||||
s = ""
|
s = ""
|
||||||
for rec_data in hexstr_to_Nbytearr(hexstr, 3):
|
for rec_data in hexstr_to_Nbytearr(hexstr, 3):
|
||||||
@@ -558,10 +606,12 @@ def format_xplmn(hexstr:Hexstr) -> str:
|
|||||||
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
|
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
|
||||||
rec_str = "unused"
|
rec_str = "unused"
|
||||||
else:
|
else:
|
||||||
rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
|
rec_str = "MCC: %03d MNC: %03d" % (
|
||||||
|
rec_info['mcc'], rec_info['mnc'])
|
||||||
s += "\t%s # %s\n" % (rec_data, rec_str)
|
s += "\t%s # %s\n" % (rec_data, rec_str)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def derive_milenage_opc(ki_hex: Hexstr, op_hex: Hexstr) -> Hexstr:
|
def derive_milenage_opc(ki_hex: Hexstr, op_hex: Hexstr) -> Hexstr:
|
||||||
"""
|
"""
|
||||||
Run the milenage algorithm to calculate OPC from Ki and OP
|
Run the milenage algorithm to calculate OPC from Ki and OP
|
||||||
@@ -578,14 +628,17 @@ def derive_milenage_opc(ki_hex:Hexstr, op_hex:Hexstr) -> Hexstr:
|
|||||||
opc_bytes = aes.encrypt(op_bytes)
|
opc_bytes = aes.encrypt(op_bytes)
|
||||||
return b2h(strxor(opc_bytes, op_bytes))
|
return b2h(strxor(opc_bytes, op_bytes))
|
||||||
|
|
||||||
|
|
||||||
def calculate_luhn(cc) -> int:
|
def calculate_luhn(cc) -> int:
|
||||||
"""
|
"""
|
||||||
Calculate Luhn checksum used in e.g. ICCID and IMEI
|
Calculate Luhn checksum used in e.g. ICCID and IMEI
|
||||||
"""
|
"""
|
||||||
num = list(map(int, str(cc)))
|
num = list(map(int, str(cc)))
|
||||||
check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
|
check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10))
|
||||||
|
for d in num[::-2]]) % 10
|
||||||
return 0 if check_digit == 10 else check_digit
|
return 0 if check_digit == 10 else check_digit
|
||||||
|
|
||||||
|
|
||||||
def mcc_from_imsi(imsi: str) -> Optional[str]:
|
def mcc_from_imsi(imsi: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
|
Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
|
||||||
@@ -598,6 +651,7 @@ def mcc_from_imsi(imsi:str) -> Optional[str]:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def mnc_from_imsi(imsi: str, long: bool = False) -> Optional[str]:
|
def mnc_from_imsi(imsi: str, long: bool = False) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
|
Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
|
||||||
@@ -613,6 +667,7 @@ def mnc_from_imsi(imsi:str, long:bool=False) -> Optional[str]:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def derive_mcc(digit1: int, digit2: int, digit3: int) -> int:
|
def derive_mcc(digit1: int, digit2: int, digit3: int) -> int:
|
||||||
"""
|
"""
|
||||||
Derive decimal representation of the MCC (Mobile Country Code)
|
Derive decimal representation of the MCC (Mobile Country Code)
|
||||||
@@ -630,6 +685,7 @@ def derive_mcc(digit1:int, digit2:int, digit3:int) -> int:
|
|||||||
|
|
||||||
return mcc
|
return mcc
|
||||||
|
|
||||||
|
|
||||||
def derive_mnc(digit1: int, digit2: int, digit3: int = 0x0f) -> int:
|
def derive_mnc(digit1: int, digit2: int, digit3: int = 0x0f) -> int:
|
||||||
"""
|
"""
|
||||||
Derive decimal representation of the MNC (Mobile Network Code)
|
Derive decimal representation of the MNC (Mobile Network Code)
|
||||||
@@ -650,6 +706,7 @@ def derive_mnc(digit1:int, digit2:int, digit3:int=0x0f) -> int:
|
|||||||
|
|
||||||
return mnc
|
return mnc
|
||||||
|
|
||||||
|
|
||||||
def dec_msisdn(ef_msisdn: Hexstr) -> Optional[Tuple[int, int, Optional[str]]]:
|
def dec_msisdn(ef_msisdn: Hexstr) -> Optional[Tuple[int, int, Optional[str]]]:
|
||||||
"""
|
"""
|
||||||
Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
|
Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
|
||||||
@@ -673,7 +730,8 @@ def dec_msisdn(ef_msisdn:Hexstr) -> Optional[Tuple[int,int,Optional[str]]]:
|
|||||||
if bcd_len == 0xff:
|
if bcd_len == 0xff:
|
||||||
return None
|
return None
|
||||||
elif bcd_len > 11 or bcd_len < 1:
|
elif bcd_len > 11 or bcd_len < 1:
|
||||||
raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
|
raise ValueError(
|
||||||
|
"Length of MSISDN (%d bytes) is out of range" % bcd_len)
|
||||||
|
|
||||||
# Parse ToN / NPI
|
# Parse ToN / NPI
|
||||||
ton = (msisdn_lhv[1] >> 4) & 0x07
|
ton = (msisdn_lhv[1] >> 4) & 0x07
|
||||||
@@ -691,6 +749,7 @@ def dec_msisdn(ef_msisdn:Hexstr) -> Optional[Tuple[int,int,Optional[str]]]:
|
|||||||
|
|
||||||
return (npi, ton, msisdn)
|
return (npi, ton, msisdn)
|
||||||
|
|
||||||
|
|
||||||
def enc_msisdn(msisdn: str, npi: int = 0x01, ton: int = 0x03) -> Hexstr:
|
def enc_msisdn(msisdn: str, npi: int = 0x01, ton: int = 0x03) -> Hexstr:
|
||||||
"""
|
"""
|
||||||
Encode MSISDN as LHV so it can be stored to EF.MSISDN.
|
Encode MSISDN as LHV so it can be stored to EF.MSISDN.
|
||||||
@@ -757,10 +816,12 @@ def dec_st(st, table="sim") -> str:
|
|||||||
# Byte X contains info about Services num (8X-7) to num (8X)
|
# Byte X contains info about Services num (8X-7) to num (8X)
|
||||||
# bit = 1: service available
|
# bit = 1: service available
|
||||||
# bit = 0: service not available
|
# bit = 0: service not available
|
||||||
avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
|
avail_st += '\tService %d - %s\n' % (
|
||||||
|
(8*i) + j, lookup_map[(8*i) + j])
|
||||||
byte = byte >> 1
|
byte = byte >> 1
|
||||||
return avail_st
|
return avail_st
|
||||||
|
|
||||||
|
|
||||||
def first_TLV_parser(bytelist):
|
def first_TLV_parser(bytelist):
|
||||||
'''
|
'''
|
||||||
first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
|
first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
|
||||||
@@ -779,6 +840,7 @@ def first_TLV_parser(bytelist):
|
|||||||
Val = bytelist[2:2+Len]
|
Val = bytelist[2:2+Len]
|
||||||
return (Tag, Len, Val)
|
return (Tag, Len, Val)
|
||||||
|
|
||||||
|
|
||||||
def TLV_parser(bytelist):
|
def TLV_parser(bytelist):
|
||||||
'''
|
'''
|
||||||
TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
|
TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
|
||||||
@@ -800,6 +862,7 @@ def TLV_parser(bytelist):
|
|||||||
bytelist = bytelist[L+2:]
|
bytelist = bytelist[L+2:]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def enc_st(st, service, state=1):
|
def enc_st(st, service, state=1):
|
||||||
"""
|
"""
|
||||||
Encodes the EF S/U/IST/EST and returns the updated Service Table
|
Encodes the EF S/U/IST/EST and returns the updated Service Table
|
||||||
@@ -843,6 +906,7 @@ def enc_st(st, service, state=1):
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def dec_addr_tlv(hexstr):
|
def dec_addr_tlv(hexstr):
|
||||||
"""
|
"""
|
||||||
Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
|
Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
|
||||||
@@ -889,6 +953,7 @@ def dec_addr_tlv(hexstr):
|
|||||||
|
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
|
|
||||||
def enc_addr_tlv(addr, addr_type='00'):
|
def enc_addr_tlv(addr, addr_type='00'):
|
||||||
"""
|
"""
|
||||||
Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
|
Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
|
||||||
@@ -916,6 +981,7 @@ def enc_addr_tlv(addr, addr_type='00'):
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def is_hex(string: str, minlen: int = 2, maxlen: Optional[int] = None) -> bool:
|
def is_hex(string: str, minlen: int = 2, maxlen: Optional[int] = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if a string is a valid hexstring
|
Check if a string is a valid hexstring
|
||||||
@@ -938,6 +1004,7 @@ def is_hex(string:str, minlen:int=2, maxlen:Optional[int]=None) -> bool:
|
|||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def sanitize_pin_adm(pin_adm, pin_adm_hex=None) -> Hexstr:
|
def sanitize_pin_adm(pin_adm, pin_adm_hex=None) -> Hexstr:
|
||||||
"""
|
"""
|
||||||
The ADM pin can be supplied either in its hexadecimal form or as
|
The ADM pin can be supplied either in its hexadecimal form or as
|
||||||
@@ -961,12 +1028,15 @@ def sanitize_pin_adm(pin_adm, pin_adm_hex = None) -> Hexstr:
|
|||||||
try:
|
try:
|
||||||
try_encode = h2b(pin_adm)
|
try_encode = h2b(pin_adm)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("PIN-ADM needs to be hex encoded using this option")
|
raise ValueError(
|
||||||
|
"PIN-ADM needs to be hex encoded using this option")
|
||||||
else:
|
else:
|
||||||
raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
|
raise ValueError(
|
||||||
|
"PIN-ADM needs to be exactly 16 digits (hex encoded)")
|
||||||
|
|
||||||
return pin_adm
|
return pin_adm
|
||||||
|
|
||||||
|
|
||||||
def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
|
def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
|
||||||
"""
|
"""
|
||||||
Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
|
Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
|
||||||
@@ -983,6 +1053,7 @@ def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='
|
|||||||
content = rpad(content, len(hexstr))
|
content = rpad(content, len(hexstr))
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def dec_ePDGSelection(sixhexbytes):
|
def dec_ePDGSelection(sixhexbytes):
|
||||||
"""
|
"""
|
||||||
Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
|
Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
|
||||||
@@ -996,15 +1067,18 @@ def dec_ePDGSelection(sixhexbytes):
|
|||||||
# first three bytes (six ascii hex chars)
|
# first three bytes (six ascii hex chars)
|
||||||
plmn_str = sixhexbytes[:plmn_chars]
|
plmn_str = sixhexbytes[:plmn_chars]
|
||||||
# two bytes after first three bytes
|
# two bytes after first three bytes
|
||||||
epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
|
epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars +
|
||||||
|
epdg_priority_chars]
|
||||||
# one byte after first five bytes
|
# one byte after first five bytes
|
||||||
epdg_fqdn_format_str = sixhexbytes[plmn_chars + epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
|
epdg_fqdn_format_str = sixhexbytes[plmn_chars +
|
||||||
|
epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
|
||||||
res['mcc'] = dec_mcc_from_plmn(plmn_str)
|
res['mcc'] = dec_mcc_from_plmn(plmn_str)
|
||||||
res['mnc'] = dec_mnc_from_plmn(plmn_str)
|
res['mnc'] = dec_mnc_from_plmn(plmn_str)
|
||||||
res['epdg_priority'] = epdg_priority_str
|
res['epdg_priority'] = epdg_priority_str
|
||||||
res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
|
res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def format_ePDGSelection(hexstr):
|
def format_ePDGSelection(hexstr):
|
||||||
ePDGSelection_info_tag_chars = 2
|
ePDGSelection_info_tag_chars = 2
|
||||||
ePDGSelection_info_tag_str = hexstr[:2]
|
ePDGSelection_info_tag_str = hexstr[:2]
|
||||||
@@ -1031,17 +1105,20 @@ def format_ePDGSelection(hexstr):
|
|||||||
|
|
||||||
content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
|
content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
|
||||||
# Right pad to prevent index out of range - multiple of 6 bytes
|
# Right pad to prevent index out of range - multiple of 6 bytes
|
||||||
content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
|
content_str = rpad(content_str, len(content_str) +
|
||||||
|
(12 - (len(content_str) % 12)))
|
||||||
for rec_data in hexstr_to_Nbytearr(content_str, 6):
|
for rec_data in hexstr_to_Nbytearr(content_str, 6):
|
||||||
rec_info = dec_ePDGSelection(rec_data)
|
rec_info = dec_ePDGSelection(rec_data)
|
||||||
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
|
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
|
||||||
rec_str = "unused"
|
rec_str = "unused"
|
||||||
else:
|
else:
|
||||||
rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
|
rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
|
||||||
(rec_info['mcc'], rec_info['mnc'], rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
|
(rec_info['mcc'], rec_info['mnc'],
|
||||||
|
rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
|
||||||
s += "\t%s # %s\n" % (rec_data, rec_str)
|
s += "\t%s # %s\n" % (rec_data, rec_str)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def get_addr_type(addr):
|
def get_addr_type(addr):
|
||||||
"""
|
"""
|
||||||
Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
|
Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
|
||||||
@@ -1093,6 +1170,7 @@ def get_addr_type(addr):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def sw_match(sw: str, pattern: str) -> bool:
|
def sw_match(sw: str, pattern: str) -> bool:
|
||||||
"""Match given SW against given pattern."""
|
"""Match given SW against given pattern."""
|
||||||
# Create a masked version of the returned status word
|
# Create a masked version of the returned status word
|
||||||
@@ -1108,6 +1186,7 @@ def sw_match(sw:str, pattern:str) -> bool:
|
|||||||
# Compare the masked version against the pattern
|
# Compare the masked version against the pattern
|
||||||
return sw_masked == pattern
|
return sw_masked == pattern
|
||||||
|
|
||||||
|
|
||||||
def tabulate_str_list(str_list, width: int = 79, hspace: int = 2, lspace: int = 1,
|
def tabulate_str_list(str_list, width: int = 79, hspace: int = 2, lspace: int = 1,
|
||||||
align_left: bool = True) -> str:
|
align_left: bool = True) -> str:
|
||||||
"""Pretty print a list of strings into a tabulated form.
|
"""Pretty print a list of strings into a tabulated form.
|
||||||
@@ -1140,17 +1219,21 @@ def tabulate_str_list(str_list, width:int = 79, hspace:int = 2, lspace:int = 1,
|
|||||||
table.append(format_str_row % tuple(str_list_row))
|
table.append(format_str_row % tuple(str_list_row))
|
||||||
return '\n'.join(table)
|
return '\n'.join(table)
|
||||||
|
|
||||||
|
|
||||||
def auto_int(x):
|
def auto_int(x):
|
||||||
"""Helper function for argparse to accept hexadecimal integers."""
|
"""Helper function for argparse to accept hexadecimal integers."""
|
||||||
return int(x, 0)
|
return int(x, 0)
|
||||||
|
|
||||||
|
|
||||||
class JsonEncoder(json.JSONEncoder):
|
class JsonEncoder(json.JSONEncoder):
|
||||||
"""Extend the standard library JSONEncoder with support for more types."""
|
"""Extend the standard library JSONEncoder with support for more types."""
|
||||||
|
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if isinstance(o, BytesIO) or isinstance(o, bytes) or isinstance(o, bytearray):
|
if isinstance(o, BytesIO) or isinstance(o, bytes) or isinstance(o, bytearray):
|
||||||
return b2h(o)
|
return b2h(o)
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
def boxed_heading_str(heading, width=80):
|
def boxed_heading_str(heading, width=80):
|
||||||
"""Generate a string that contains a boxed heading."""
|
"""Generate a string that contains a boxed heading."""
|
||||||
# Auto-enlarge box if heading exceeds length
|
# Auto-enlarge box if heading exceeds length
|
||||||
@@ -1164,12 +1247,12 @@ def boxed_heading_str(heading, width=80):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DataObject(abc.ABC):
|
class DataObject(abc.ABC):
|
||||||
"""A DataObject (DO) in the sense of ISO 7816-4. Contrary to 'normal' TLVs where one
|
"""A DataObject (DO) in the sense of ISO 7816-4. Contrary to 'normal' TLVs where one
|
||||||
simply has any number of different TLVs that may occur in any order at any point, ISO 7816
|
simply has any number of different TLVs that may occur in any order at any point, ISO 7816
|
||||||
has the habit of specifying TLV data but with very spcific ordering, or specific choices of
|
has the habit of specifying TLV data but with very spcific ordering, or specific choices of
|
||||||
tags at specific points in a stream. This class tries to represent this."""
|
tags at specific points in a stream. This class tries to represent this."""
|
||||||
|
|
||||||
def __init__(self, name: str, desc: str = None, tag: int = None):
|
def __init__(self, name: str, desc: str = None, tag: int = None):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@@ -1236,7 +1319,8 @@ class DataObject(abc.ABC):
|
|||||||
bytes remaining at end of 'do' after parsing one TLV/DO.
|
bytes remaining at end of 'do' after parsing one TLV/DO.
|
||||||
"""
|
"""
|
||||||
if do[0] != self.tag:
|
if do[0] != self.tag:
|
||||||
raise ValueError('%s: Can only decode tag 0x%02x' % (self, self.tag))
|
raise ValueError('%s: Can only decode tag 0x%02x' %
|
||||||
|
(self, self.tag))
|
||||||
length = do[1]
|
length = do[1]
|
||||||
val = do[2:2+length]
|
val = do[2:2+length]
|
||||||
self.from_bytes(val)
|
self.from_bytes(val)
|
||||||
@@ -1270,8 +1354,10 @@ class DataObject(abc.ABC):
|
|||||||
def encode(self) -> bytes:
|
def encode(self) -> bytes:
|
||||||
return self.to_tlv()
|
return self.to_tlv()
|
||||||
|
|
||||||
|
|
||||||
class TL0_DataObject(DataObject):
|
class TL0_DataObject(DataObject):
|
||||||
"""Data Object that has Tag, Len=0 and no Value part."""
|
"""Data Object that has Tag, Len=0 and no Value part."""
|
||||||
|
|
||||||
def __init__(self, name: str, desc: str, tag: int, val=None):
|
def __init__(self, name: str, desc: str, tag: int, val=None):
|
||||||
super().__init__(name, desc, tag)
|
super().__init__(name, desc, tag)
|
||||||
self.val = val
|
self.val = val
|
||||||
@@ -1289,6 +1375,7 @@ class DataObjectCollection:
|
|||||||
"""A DataObjectCollection consits of multiple Data Objects identified by their tags.
|
"""A DataObjectCollection consits of multiple Data Objects identified by their tags.
|
||||||
A given encoded DO may contain any of them in any order, and may contain multiple instances
|
A given encoded DO may contain any of them in any order, and may contain multiple instances
|
||||||
of each DO."""
|
of each DO."""
|
||||||
|
|
||||||
def __init__(self, name: str, desc: str = None, members=None):
|
def __init__(self, name: str, desc: str = None, members=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.desc = desc
|
self.desc = desc
|
||||||
@@ -1352,10 +1439,12 @@ class DataObjectCollection:
|
|||||||
res.append(obj.to_tlv())
|
res.append(obj.to_tlv())
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class DataObjectChoice(DataObjectCollection):
|
class DataObjectChoice(DataObjectCollection):
|
||||||
"""One Data Object from within a choice, identified by its tag.
|
"""One Data Object from within a choice, identified by its tag.
|
||||||
This means that exactly one member of the choice must occur, and which one occurs depends
|
This means that exactly one member of the choice must occur, and which one occurs depends
|
||||||
on the tag."""
|
on the tag."""
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
"""We overload the add operator here to avoid inheriting it from DataObjecCollection."""
|
"""We overload the add operator here to avoid inheriting it from DataObjecCollection."""
|
||||||
raise TypeError
|
raise TypeError
|
||||||
@@ -1395,11 +1484,13 @@ class DataObjectChoice(DataObjectCollection):
|
|||||||
obj = self.members_by_name(decoded[0])
|
obj = self.members_by_name(decoded[0])
|
||||||
return obj.to_tlv()
|
return obj.to_tlv()
|
||||||
|
|
||||||
|
|
||||||
class DataObjectSequence:
|
class DataObjectSequence:
|
||||||
"""A sequence of DataObjects or DataObjectChoices. This allows us to express a certain
|
"""A sequence of DataObjects or DataObjectChoices. This allows us to express a certain
|
||||||
ordered sequence of DOs or choices of DOs that have to appear as per the specification.
|
ordered sequence of DOs or choices of DOs that have to appear as per the specification.
|
||||||
By wrapping them into this formal DataObjectSequence, we can offer convenience methods
|
By wrapping them into this formal DataObjectSequence, we can offer convenience methods
|
||||||
for encoding or decoding an entire sequence."""
|
for encoding or decoding an entire sequence."""
|
||||||
|
|
||||||
def __init__(self, name: str, desc: str = None, sequence=None):
|
def __init__(self, name: str, desc: str = None, sequence=None):
|
||||||
self.sequence = sequence or []
|
self.sequence = sequence or []
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -1469,8 +1560,10 @@ class DataObjectSequence:
|
|||||||
i += 1
|
i += 1
|
||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
|
|
||||||
class CardCommand:
|
class CardCommand:
|
||||||
"""A single card command / instruction."""
|
"""A single card command / instruction."""
|
||||||
|
|
||||||
def __init__(self, name, ins, cla_list=None, desc=None):
|
def __init__(self, name, ins, cla_list=None, desc=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.ins = ins
|
self.ins = ins
|
||||||
@@ -1503,6 +1596,7 @@ class CardCommand:
|
|||||||
|
|
||||||
class CardCommandSet:
|
class CardCommandSet:
|
||||||
"""A set of card instructions, typically specified within one spec."""
|
"""A set of card instructions, typically specified within one spec."""
|
||||||
|
|
||||||
def __init__(self, name, cmds=[]):
|
def __init__(self, name, cmds=[]):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.cmds = {c.ins: c for c in cmds}
|
self.cmds = {c.ins: c for c in cmds}
|
||||||
@@ -1523,7 +1617,8 @@ class CardCommandSet:
|
|||||||
for c in other.cmds.keys():
|
for c in other.cmds.keys():
|
||||||
self.cmds[c] = other.cmds[c]
|
self.cmds[c] = other.cmds[c]
|
||||||
else:
|
else:
|
||||||
raise ValueError('%s: Unsupported type to add operator: %s' % (self, other))
|
raise ValueError(
|
||||||
|
'%s: Unsupported type to add operator: %s' % (self, other))
|
||||||
|
|
||||||
def lookup(self, ins, cla=None):
|
def lookup(self, ins, cla=None):
|
||||||
"""look-up the command within the CommandSet."""
|
"""look-up the command within the CommandSet."""
|
||||||
@@ -1535,6 +1630,7 @@ class CardCommandSet:
|
|||||||
return None
|
return None
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
def all_subclasses(cls) -> set:
|
def all_subclasses(cls) -> set:
|
||||||
"""Recursively get all subclasses of a specified class"""
|
"""Recursively get all subclasses of a specified class"""
|
||||||
return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() for s in all_subclasses(c)])
|
return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() for s in all_subclasses(c)])
|
||||||
|
|||||||
Reference in New Issue
Block a user