1 Commits

Author SHA1 Message Date
Harald Welte
46bc6d25d6 HACK: support for SIM factory file formats
Change-Id: I23cd37fc06b6e2d21964fd4f2694d9ee3c6012d4
2020-09-15 10:47:45 +02:00
26 changed files with 516 additions and 813 deletions

View File

@@ -40,8 +40,8 @@ pysim requires:
Example for Debian:
apt-get install python3-pyscard python3-serial python3-pip python3-yaml
pip3 install pytlv
apt-get install python-pyscard python-serial python-pip
pip install pytlv
Mailing List

View File

@@ -9,7 +9,7 @@ if [ ! -d "./pysim-testdata/" ] ; then
exit 1
fi
virtualenv -p python3 venv --system-site-packages
virtualenv -p python2 venv --system-site-packages
. venv/bin/activate
pip install pytlv
pip install pyyaml

141
execute_ipr.py Executable file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/python3
from pySim.transport.pcsc import PcscSimLink
from pySim.utils import enc_iccid, enc_imsi
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException
from smartcard.System import readers
from lark import Lark, Transformer, Token, Tree
import sys
from format_ipr import ScriptFormatIPR
class DataTransform():
''' Transform raw/logical input data into the format use on the SIM card,
like encoding the PIN from '1234' -> 3132334 or IMSI encoding'''
def transform(self, inp):
outp = {}
for k in inp.keys():
f = getattr(self, 'xfrm_'+k, None)
if f != None:
outp[k] = f(inp[k])
else:
outp[k] = inp[k]
return outp
def xfrm_PIN(self, pin):
ret = ''
for c in str(pin):
ret += '3%c' % c
return ret
def xfrm_PIN1(self, pin):
return self.xfrm_PIN(pin)
def xfrm_PIN2(self, pin):
return self.xfrm_PIN(pin)
def xfrm_PUK1(self, pin):
return self.xfrm_PIN(pin)
def xfrm_PUK2(self, pin):
return self.xfrm_PIN(pin)
def xfrm_ADM1(self, pin):
return self.xfrm_PIN(pin)
def xfrm_ADM2(self, pin):
return self.xfrm_PIN(pin)
def xfrm_IMSI(self, imsi):
return enc_imsi(imsi)
def xfrm_ICCID(self, iccid):
# TODO: calculate luhn check digit
return enc_iccid(iccid)
def expand_cmd_template(cmd, templates):
''' Take a single command, supstituting all [] template keys with data from 'template' '''
ret = ""
for e in cmd:
if e[0] == 'hexstr':
ret += e[1]
if e[0] == 'key':
ret += templates[e[1]]
return ret
def match_sw(actual_sw, sw_match):
''' Check if actual_sw matches any of the templates given in sw_match'''
def match_sw_single(actual_sw, match):
match = match.lower()
if 'x' in match:
FIXME
else:
if actual_sw.lower() == match:
return True
return False
if sw_match == []:
return True
for m in sw_match:
if match_sw_single(actual_sw, m):
return True
return False
def execute_ipr_raw(s, sl, dynamic_data_raw = {}):
""" translate a single LDR statement to IPR format. """
if s == None:
None
elif s == 'reset':
print("RESET")
sl.reset_card()
elif s[0] == 'rem':
print("REM %s" % (s[1]))
elif s[0] == 'cmd':
d = s[1]
req = expand_cmd_template(d['req'], dynamic_data_raw)
rsp = d['rsp']
print("\tREQ: %s, EXP: %s" % (req, rsp))
(data, sw) = sl.send_apdu_raw(req)
if not match_sw(sw, rsp):
raise ValueError("SW %s doesn't match expected %s" % (sw, rsp))
print("\tRSP: %s\n" % (sw))
def execute_ipr(s, sl, dynamic_data = {}):
""" translate a single LDR statement to IPR format; optionally substitute dynamic_data. """
xf = DataTransform()
return execute_ipr_raw(s, sl, xf.transform(dynamic_data))
'''Dictionaries like this must be generated for each card to be programmed'''
demo_dict = {
'PIN1': '1234',
'PIN2': '1234',
'PUK1': '12345678',
'PUK2': '12345678',
'ADM1': '11111111',
'KIC1': '100102030405060708090a0b0c0d0e0f',
'KID1': '101102030405060708090a0b0c0d0e0f',
'KIK1': '102102030405060708090a0b0c0d0e0f',
'KIC2': '200102030405060708090a0b0c0d0e0f',
'KID2': '201102030405060708090a0b0c0d0e0f',
'KIK2': '202102030405060708090a0b0c0d0e0f',
'KIC3': '300102030405060708090a0b0c0d0e0f',
'KID3': '301102030405060708090a0b0c0d0e0f',
'KIK3': '302102030405060708090a0b0c0d0e0f',
'ICCID': '012345678901234567',
'IMSI': '001010123456789',
'ACC': '0200',
'KI': '000102030405060708090a0b0c0d0e0f',
'OPC': '101112131415161718191a1b1c1d1e1f',
'VERIFY_ICCID': '0001020304050608090a0b0c0d0e0f',
}
sl = PcscSimLink(0)
infile_name = sys.argv[1]
fmt = ScriptFormatIPR()
fmt.parse_process_file(infile_name, execute_ipr, {'sl':sl, 'dynamic_data':demo_dict})

55
format_ipr.py Normal file
View File

@@ -0,0 +1,55 @@
from lark import Lark, Transformer, Token, Tree
from script_format import ScriptFormat
from format_ldr import LdrXfrm
class IprXfrm(LdrXfrm):
""" transform the parse tree into a more easily consumable form """
def key(self, items):
return ('key', ''.join(list(items)))
def req(self, items):
return items[:-1]
def rsp(self, items):
return items[:-1]
#def NEWLINE(self, items):
#return None
class ScriptFormatIPR(ScriptFormat):
# parser for the IPR file format as used by the SIM card factory
ipr_parser = Lark(r"""
script: statement*
?statement: cmd | rst | rem | NEWLINE
NONL: /[^\n]/+
rem: "//" NONL? NEWLINE
ALNUM: DIGIT | LETTER | "_"
key: "[" ALNUM+ "]"
cmd: req rsp
req: "I:" [hexstr|key]+ NEWLINE
hexstr: HEX_ITEM+
HEX_ITEM: HEXDIGIT ~ 2
rsp: "O:" swpattern? NEWLINE
swpattern: HEX_OR_X ~ 4
HEX_OR_X: HEXDIGIT | "X" | "x"
rst: "RESET" NEWLINE
%import common.ESCAPED_STRING -> STRING
%import common.WS_INLINE
%import common.HEXDIGIT
%import common.DIGIT
%import common.LETTER
%import common.NEWLINE
%ignore WS_INLINE
""", start='script', parser='lalr')#, lexer='standard')
def parse_xform(self, text):
tree = self.ipr_parser.parse(text)
#print(tree.pretty())
p = IprXfrm().transform(tree)
return p

74
format_ldr.py Normal file
View File

@@ -0,0 +1,74 @@
from lark import Lark, Transformer, Token, Tree
from script_format import ScriptFormat
class LdrXfrm(Transformer):
""" transform the parse tree into a more easily consumable form """
def rst(self, items):
return ('reset')
def NONL(self, items):
return ''.join([i for i in items.value])
def rem(self, items):
return ('rem', items[0])
def swmatch(self, items):
return ('swmatch', items)
def cmd(self, items):
return ('cmd', {'req': items[0], 'rsp': items[1]})
def todigit(self, item):
""" convert from Token/Tree to raw hex-digit """
if isinstance(item, Token):
return item.value
elif isinstance(item, Tree):
return item.data
def hex_item(self, items):
""" return one byte as two-digit HEX string """
return "%s%s" % (items[0].value, items[1].value)
def hexstr(self, items):
""" return list of two-digit HEX strings """
return ('hexstr', ''.join(list(items)))
def swpattern(self, items):
""" return list of four HEX nibbles (or 'x' as wildcard) """
arr = [self.todigit(x) for x in items]
return ''.join(arr)
class ScriptFormatLDR(ScriptFormat):
# parser for the LDR file format as generated by Simulity Profile Editor
ldr_parser = Lark(r"""
script: statement*
?statement: cmd | rst | rem | NEWLINE
BSLASH: "\\\n"
%ignore BSLASH
NONL: /[^\n]/+
rem: "REM" NONL? NEWLINE
ALNUM: DIGIT | LETTER | "_"
key: "[" ALNUM+ "]"
cmd: "CMD" hexstr [swmatch] NEWLINE
cmd_item: hexstr | key
hexstr: hex_item+
hex_item: HEXDIGIT HEXDIGIT
swmatch: "(" swpattern ("," swpattern)* ")"
swpattern: hex_or_x hex_or_x hex_or_x hex_or_x
?hex_or_x: HEXDIGIT | "X" -> x | "x" -> x
rst: "RST" NEWLINE
%import common.ESCAPED_STRING -> STRING
%import common.WS_INLINE
%import common.HEXDIGIT
%import common.DIGIT
%import common.LETTER
%import common.NEWLINE
%ignore WS_INLINE
""", start='script')
def parse_xform(self, text):
tree = self.ldr_parser.parse(text)
#print(tree.pretty())
p = LdrXfrm().transform(tree)
return p

84
ldr_to_ipr.py Executable file
View File

@@ -0,0 +1,84 @@
#!/usr/bin/python3
from lark import Lark, Transformer, Token, Tree
import sys
from format_ldr import ScriptFormatLDR
from format_ipr import ScriptFormatIPR
def split_hex(value):
""" split a string of hex digits into groups (bytes) of two digits. """
return ' '.join(value[i:i+2] for i in range(0, len(value), 2))
def expand_cmd(cmd):
ret = ""
for e in cmd:
if e[0] == 'hexstr':
ret += e[1]
else:
raise ValueError("Unsupported '%s'" % (e[0]))
return ret
def ldr_stmt_to_ipr(s):
""" translate a single LDR statement to IPR format. """
if s == None:
None
elif s == 'reset':
print("RESET")
print("")
elif s[0] == 'rem':
print("//\t%s" % s[1])
elif s[0] == 'cmd':
cmd = s[1]
req = cmd['req']
rsp = cmd['rsp']
print("I: %s" % split_hex(expand_cmd([req])))
if rsp != None and len(rsp) != 1:
if rsp[0] != 'swmatch' or len(rsp[1]) != 1:
raise ValueError("Unsupported '%s'" % (rsp))
print("O: %s" % rsp[1][0])
else:
print("O:")
print("")
else:
print("Unknown %s" % (s.pretty()))
raise ValueError()
test_text = '''
RST
CMD E0 CA DF 1F 13
CMD E0 CA DF 1F (90 00)
CMD E0 CA DF 1F (61 XX, 90 00)
REM foo bar
CMD E4 DA DF 20 09 EA 53 F8 D7 64 1E D9 88 00 \\
(90 00 , 6B 00)
'''
def run_statement(s):
print(s)
def fii(s):
if s.data == 'rst':
print("=> RESET")
# FIXME: actually perform card reset
elif s.data == 'rem':
print(s)
elif s.data == 'cmd':
#print(s)
cmd = s.children[0]
print(s.pretty())
# FIXME: if swmatch: match all contained swpattern
else:
print("Unknown %s" % (s.pretty()))
raise ValueError()
#process_ldr(test_text, run_statement)
#process_ldr(test_text, ldr_stmt_to_ipr)
fmt = ScriptFormatLDR()
fmt.parse_process_file(sys.argv[1], ldr_stmt_to_ipr)
#fmt.parse_process_file(sys.argv[1], run_statement)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python2
#
# Utility to deal with sim cards and program the 'magic' ones easily
@@ -147,24 +147,6 @@ def parse_options():
parser.add_option("--acc", dest="acc",
help="Set ACC bits (Access Control Code). not all card types are supported",
)
parser.add_option("--epdgid", dest="epdgid",
help="Set Home Evolved Packet Data Gateway (ePDG) Identifier. (Only FQDN format supported)",
)
parser.add_option("--epdgSelection", dest="epdgSelection",
help="Set PLMN for ePDG Selection Information. (Only Operator Identifier FQDN format supported)",
)
parser.add_option("--pcscf", dest="pcscf",
help="Set Proxy Call Session Control Function (P-CSCF) Address. (Only FQDN format supported)",
)
parser.add_option("--ims-hdomain", dest="ims_hdomain",
help="Set IMS Home Network Domain Name in FQDN format",
)
parser.add_option("--impi", dest="impi",
help="Set IMS private user identity",
)
parser.add_option("--impu", dest="impu",
help="Set IMS public user identity",
)
parser.add_option("--read-imsi", dest="read_imsi", action="store_true",
help="Read the IMSI from the CARD", default=False
)
@@ -240,9 +222,8 @@ def parse_options():
def _digits(secret, usage, len, num):
seed = secret + usage + '%d' % num
s = hashlib.sha1(seed.encode())
d = ''.join(['%02d' % x for x in s.digest()])
s = hashlib.sha1(secret + usage + '%d' % num)
d = ''.join(['%02d'%ord(x) for x in s.digest()])
return d[0:len]
def _mcc_mnc_digits(mcc, mnc):
@@ -276,7 +257,7 @@ def _dbi_binary_quote(s):
m = sum_
e = i
if m == 0: # No overhead ? use this !
break
break;
# Generate output
out = []
@@ -319,7 +300,7 @@ def gen_parameters(opts):
if opts.name is not None:
if len(opts.name) > 16:
raise ValueError('Service Provider Name must max 16 characters!')
raise ValueError('Service Provider Name must max 16 characters!');
if opts.msisdn is not None:
msisdn = opts.msisdn
@@ -336,7 +317,7 @@ def gen_parameters(opts):
if opts.iccid is not None:
iccid = opts.iccid
if not _isnum(iccid, 19) and not _isnum(iccid, 20):
raise ValueError('ICCID must be 19 or 20 digits !')
raise ValueError('ICCID must be 19 or 20 digits !');
else:
if opts.num is None:
@@ -448,15 +429,6 @@ def gen_parameters(opts):
pin_adm = sanitize_pin_adm(opts)
# ePDG Selection Information
if opts.epdgSelection:
if len(opts.epdgSelection) < 5 or len(opts.epdgSelection) > 6:
raise ValueError('ePDG Selection Information is not valid')
epdg_mcc = opts.epdgSelection[:3]
epdg_mnc = opts.epdgSelection[3:]
if not epdg_mcc.isdigit() or not epdg_mnc.isdigit():
raise ValueError('PLMN for ePDG Selection must only contain decimal digits')
# Return that
return {
'name' : opts.name,
@@ -470,12 +442,6 @@ def gen_parameters(opts):
'acc' : acc,
'pin_adm' : pin_adm,
'msisdn' : opts.msisdn,
'epdgid' : opts.epdgid,
'epdgSelection' : opts.epdgSelection,
'pcscf' : opts.pcscf,
'ims_hdomain': opts.ims_hdomain,
'impi' : opts.impi,
'impu' : opts.impu,
}
@@ -522,15 +488,15 @@ def _read_params_csv(opts, iccid=None, imsi=None):
if opts.num is not None and opts.read_iccid is False and opts.read_imsi is False:
if opts.num == i:
f.close()
return row
return row;
i += 1
if row['iccid'] == iccid:
f.close()
return row
return row;
if row['imsi'] == imsi:
f.close()
return row
return row;
f.close()
return None
@@ -677,13 +643,13 @@ def process_card(opts, first, card_handler):
if opts.read_iccid:
if opts.dry_run:
# Connect transport
card_handler.get(False)
card_handler.get(false)
(res,_) = scc.read_binary(['3f00', '2fe2'], length=10)
iccid = dec_iccid(res)
elif opts.read_imsi:
if opts.dry_run:
# Connect transport
card_handler.get(False)
card_handler.get(false)
(res,_) = scc.read_binary(EF['IMSI'])
imsi = swap_nibbles(res)[3:]
else:
@@ -720,8 +686,6 @@ if __name__ == '__main__':
# Init card reader driver
sl = init_reader(opts)
if sl is None:
exit(1)
# Create command layer
scc = SimCardCommands(transport=sl)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python2
#
# Utility to display some informations about a SIM card
@@ -28,15 +28,14 @@ import os
import random
import re
import sys
from pySim.ts_51_011 import EF, DF, EF_SST_map, EF_AD_mode_map
from pySim.ts_51_011 import EF, DF, EF_SST_map
from pySim.ts_31_102 import EF_UST_map, EF_USIM_ADF_map
from pySim.ts_31_103 import EF_IST_map, EF_ISIM_ADF_map
from pySim.ts_31_103 import EF_IST_map
from pySim.commands import SimCardCommands
from pySim.cards import card_detect, Card, UsimCard, IsimCard
from pySim.cards import card_detect, Card
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_msisdn
from pySim.utils import format_xplmn_w_act, dec_spn, dec_st, init_reader, dec_addr_tlv
from pySim.utils import h2s, format_ePDGSelection
from pySim.utils import format_xplmn_w_act, dec_spn, dec_st, init_reader, dec_epdgid
def parse_options():
@@ -82,8 +81,6 @@ if __name__ == '__main__':
# Init card reader driver
sl = init_reader(opts)
if sl is None:
exit(1)
# Create command layer
scc = SimCardCommands(transport=sl)
@@ -228,15 +225,7 @@ if __name__ == '__main__':
# EF.AD
(res, sw) = card.read_binary('AD')
if sw == '9000':
print("Administrative data: %s" % (res,))
if res[:2] in EF_AD_mode_map:
print("\tMS operation mode: %s" % (EF_AD_mode_map[res[:2]],))
else:
print("\tMS operation mode: (unknown 0x%s)" % (res[:2],))
if int(res[4:6], 16) & 0x01:
print("\tCiphering Indicator: enabled")
else:
print("\tCiphering Indicator: disabled")
print("AD: %s" % (res,))
else:
print("AD: Can't read, response code = %s" % (sw,))
@@ -252,104 +241,33 @@ if __name__ == '__main__':
# Check whether we have th AID of USIM, if so select it by its AID
# EF.UST - File Id in ADF USIM : 6f38
if '9000' == card.select_adf_by_aid():
# Select USIM profile
usim_card = UsimCard(scc)
# EF.EHPLMN
if usim_card.file_exists(EF_USIM_ADF_map['EHPLMN']):
(res, sw) = usim_card.read_ehplmn()
if card.file_exists(EF_USIM_ADF_map['EHPLMN']):
(res, sw) = card.read_ehplmn()
if sw == '9000':
print("EHPLMN:\n%s" % (res))
else:
print("EHPLMN: Can't read, response code = %s" % (sw,))
# EF.UST
try:
if usim_card.file_exists(EF_USIM_ADF_map['UST']):
# res[0] - EF content of UST
# res[1] - Human readable format of services marked available in UST
(res, sw) = usim_card.read_ust()
if sw == '9000':
print("USIM Service Table: %s" % res[0])
print("%s" % res[1])
else:
print("USIM Service Table: Can't read, response code = %s" % (sw,))
except Exception as e:
print("USIM Service Table: Can't read file -- " + str(e))
(res, sw) = card.read_binary(EF_USIM_ADF_map['UST'])
if sw == '9000':
print("USIM Service Table: %s" % res)
# Print those which are available
print("%s" % dec_st(res, table="usim"))
else:
print("USIM Service Table: Can't read, response code = %s" % (sw,))
#EF.ePDGId - Home ePDG Identifier
try:
if usim_card.file_exists(EF_USIM_ADF_map['ePDGId']):
(res, sw) = usim_card.read_epdgid()
if sw == '9000':
print("ePDGId:\n%s" % (len(res) and res or '\tNot available\n',))
else:
print("ePDGId: Can't read, response code = %s" % (sw,))
(res, sw) = card.read_binary(EF_USIM_ADF_map['ePDGId'])
if sw == '9000':
content = dec_epdgid(res)
print("ePDGId:\n%s" % (len(content) and content or '\tNot available\n',))
else:
print("ePDGId: Can't read, response code = %s" % (sw,))
except Exception as e:
print("ePDGId: Can't read file -- " + str(e))
#EF.ePDGSelection - ePDG Selection Information
try:
if usim_card.file_exists(EF_USIM_ADF_map['ePDGSelection']):
(res, sw) = usim_card.read_ePDGSelection()
if sw == '9000':
print("ePDGSelection:\n%s" % (res,))
else:
print("ePDGSelection: Can't read, response code = %s" % (sw,))
except Exception as e:
print("ePDGSelection: Can't read file -- " + str(e))
# Select ISIM application by its AID
if '9000' == card.select_adf_by_aid(adf="isim"):
# Select USIM profile
isim_card = IsimCard(scc)
#EF.P-CSCF - P-CSCF Address
try:
if isim_card.file_exists(EF_ISIM_ADF_map['PCSCF']):
res = isim_card.read_pcscf()
print("P-CSCF:\n%s" % (len(res) and res or '\tNot available\n',))
except Exception as e:
print("P-CSCF: Can't read file -- " + str(e))
# EF.DOMAIN - Home Network Domain Name e.g. ims.mncXXX.mccXXX.3gppnetwork.org
try:
if isim_card.file_exists(EF_ISIM_ADF_map['DOMAIN']):
(res, sw) = isim_card.read_domain()
if sw == '9000':
print("Home Network Domain Name: %s" % (len(res) and res or 'Not available',))
else:
print("Home Network Domain Name: Can't read, response code = %s" % (sw,))
except Exception as e:
print("Home Network Domain Name: Can't read file -- " + str(e))
# EF.IMPI - IMS private user identity
try:
if isim_card.file_exists(EF_ISIM_ADF_map['IMPI']):
(res, sw) = isim_card.read_impi()
if sw == '9000':
print("IMS private user identity: %s" % (len(res) and res or 'Not available',))
else:
print("IMS private user identity: Can't read, response code = %s" % (sw,))
except Exception as e:
print("IMS private user identity: Can't read file -- " + str(e))
# EF.IMPU - IMS public user identity
try:
if isim_card.file_exists(EF_ISIM_ADF_map['IMPU']):
res = isim_card.read_impu()
print("IMS public user identity:\n%s" % (len(res) and res or '\tNot available\n',))
except Exception as e:
print("IMS public user identity: Can't read file -- " + str(e))
# EF.UICCIARI - UICC IARI
try:
if isim_card.file_exists(EF_ISIM_ADF_map['UICCIARI']):
res = isim_card.read_iari()
print("UICC IARI:\n%s" % (len(res) and res or '\tNot available\n',))
except Exception as e:
print("UICC IARI: Can't read file -- " + str(e))
# Check whether we have th AID of ISIM, if so select it by its AID
# EF.IST - File Id in ADF ISIM : 6f07
if '9000' == card.select_adf_by_aid(adf="isim"):

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
""" pySim: card handler utilities

View File

@@ -25,10 +25,8 @@
from pySim.ts_51_011 import EF, DF
from pySim.ts_31_102 import EF_USIM_ADF_map
from pySim.ts_31_103 import EF_ISIM_ADF_map
from pySim.utils import *
from smartcard.util import toBytes
from pytlv.TLV import *
class Card(object):
@@ -206,6 +204,30 @@ class Card(object):
else:
return (None, sw)
# Read the (full) AID for either ISIM or USIM or ISIM application
def read_aid(self, isim = False):
# First (known) halves of the AID
aid_usim = "a0000000871002"
aid_isim = "a0000000871004"
# Select which one to look for
if isim:
aid = aid_isim
else:
aid = aid_usim
# Find out how many records the EF.DIR has, then go through
# all records and try to find the AID we are looking for
aid_record_count = self._scc.record_count(['2F00'])
for i in range(0, aid_record_count):
record = self._scc.read_record(['2F00'], i + 1)
if aid in record[0]:
aid_len = int(record[0][6:8], 16)
return record[0][8:8 + aid_len * 2]
return None
# Fetch all the AIDs present on UICC
def read_aids(self):
try:
@@ -266,177 +288,7 @@ class UsimCard(Card):
data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
return sw
def read_epdgid(self):
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGId'])
if sw == '9000':
return (dec_addr_tlv(res), sw)
else:
return (None, sw)
def update_epdgid(self, epdgid):
size = self._scc.binary_size(EF_USIM_ADF_map['ePDGId']) * 2
if len(epdgid) > 0:
addr_type = get_addr_type(epdgid)
if addr_type == None:
raise ValueError("Unknown ePDG Id address type or invalid address provided")
epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
else:
epdgid_tlv = rpad('ff', size)
data, sw = self._scc.update_binary(
EF_USIM_ADF_map['ePDGId'], epdgid_tlv)
return sw
def read_ePDGSelection(self):
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGSelection'])
if sw == '9000':
return (format_ePDGSelection(res), sw)
else:
return (None, sw)
def update_ePDGSelection(self, mcc, mnc):
(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):
# Reset contents
# 80 - Tag value
(res, sw) = self._scc.update_binary(EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
elif sw == '9000':
(res, sw) = self._scc.update_binary(EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
return sw
def read_ust(self):
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
if sw == '9000':
# Print those which are available
return ([res, dec_st(res, table="usim")], sw)
else:
return ([None, None], sw)
def update_ust(self, service, bit=1):
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
if sw == '9000':
content = enc_st(res, service, bit)
(res, sw) = self._scc.update_binary(EF_USIM_ADF_map['UST'], content)
return sw
class IsimCard(Card):
def __init__(self, ssc):
super(IsimCard, self).__init__(ssc)
def read_pcscf(self):
rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['PCSCF'])
pcscf_recs = ""
for i in range(0, rec_cnt):
(res, sw) = self._scc.read_record(EF_ISIM_ADF_map['PCSCF'], i + 1)
if sw == '9000':
content = dec_addr_tlv(res)
pcscf_recs += "%s" % (len(content) and content or '\tNot available\n')
else:
pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (sw)
return pcscf_recs
def update_pcscf(self, pcscf):
if len(pcscf) > 0:
addr_type = get_addr_type(pcscf)
if addr_type == None:
raise ValueError("Unknown PCSCF address type or invalid address provided")
content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
else:
# Just the tag value
content = '80'
rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
pcscf_tlv = rpad(content, rec_size_bytes*2)
data, sw = self._scc.update_record(EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
return sw
def read_domain(self):
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
if sw == '9000':
# Skip the inital tag value ('80') byte and get length of contents
length = int(res[2:4], 16)
content = h2s(res[4:4+(length*2)])
return (content, sw)
else:
return (None, sw)
def update_domain(self, domain=None, mcc=None, mnc=None):
hex_str = ""
if domain:
hex_str = s2h(domain)
elif mcc and mnc:
# MCC and MNC always has 3 digits in domain form
plmn_str = 'mnc' + lpad(mnc, 3, "0") + '.mcc' + lpad(mcc, 3, "0")
hex_str = s2h('ims.' + plmn_str + '.3gppnetwork.org')
# Build TLV
tlv = TLV(['80'])
content = tlv.build({'80': hex_str})
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))
return sw
def read_impi(self):
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
if sw == '9000':
# Skip the inital tag value ('80') byte and get length of contents
length = int(res[2:4], 16)
content = h2s(res[4:4+(length*2)])
return (content, sw)
else:
return (None, sw)
def update_impi(self, impi=None):
hex_str = ""
if impi:
hex_str = s2h(impi)
# Build TLV
tlv = TLV(['80'])
content = tlv.build({'80': hex_str})
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))
return sw
def read_impu(self):
rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['IMPU'])
impu_recs = ""
for i in range(0, rec_cnt):
(res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
if sw == '9000':
# Skip the inital tag value ('80') byte and get length of contents
length = int(res[2:4], 16)
content = h2s(res[4:4+(length*2)])
impu_recs += "\t%s\n" % (len(content) and content or 'Not available')
else:
impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (sw)
return impu_recs
def update_impu(self, impu=None):
hex_str = ""
if impu:
hex_str = s2h(impu)
# Build TLV
tlv = TLV(['80'])
content = tlv.build({'80': hex_str})
rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
impu_tlv = rpad(content, rec_size_bytes*2)
data, sw = self._scc.update_record(EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
return sw
def read_iari(self):
rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
uiari_recs = ""
for i in range(0, rec_cnt):
(res, sw) = self._scc.read_record(EF_ISIM_ADF_map['UICCIARI'], i + 1)
if sw == '9000':
# Skip the inital tag value ('80') byte and get length of contents
length = int(res[2:4], 16)
content = h2s(res[4:4+(length*2)])
uiari_recs += "\t%s\n" % (len(content) and content or 'Not available')
else:
uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (sw)
return uiari_recs
class _MagicSimBase(Card):
"""
@@ -481,7 +333,7 @@ class _MagicSimBase(Card):
r = self._scc.select_file(['3f00', '7f4d', f[0]])
rec_len = int(r[-1][28:30], 16)
tlen = int(r[-1][4:8],16)
rec_cnt = (tlen / rec_len) - 1
rec_cnt = (tlen / rec_len) - 1;
if (rec_cnt < 1) or (rec_len != f[1]):
raise RuntimeError('Bad card type')
@@ -611,7 +463,7 @@ class FakeMagicSim(Card):
r = self._scc.select_file(['3f00', '000c'])
rec_len = int(r[-1][28:30], 16)
tlen = int(r[-1][4:8],16)
rec_cnt = (tlen / rec_len) - 1
rec_cnt = (tlen / rec_len) - 1;
if (rec_cnt < 1) or (rec_len != 0x5a):
raise RuntimeError('Bad card type')
@@ -772,9 +624,9 @@ class SysmoSIMgr2(Card):
def program(self, p):
# select MF
# select MF
r = self._scc.select_file(['3f00'])
# authenticate as SUPER ADM using default key
self._scc.verify_chv(0x0b, h2b("3838383838383838"))
@@ -790,7 +642,7 @@ class SysmoSIMgr2(Card):
pdu = 'A0D43A0508' + b2h(pin)
data, sw = self._scc._tp.send_apdu(pdu)
# authenticate as ADM (enough to write file, and can set PINs)
self._scc.verify_chv(0x05, pin)
@@ -800,7 +652,7 @@ class SysmoSIMgr2(Card):
# select DF_GSM
r = self._scc.select_file(['7f20'])
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
@@ -822,7 +674,7 @@ class SysmoSIMgr2(Card):
# select DF_TELECOM
r = self._scc.select_file(['3f00', '7f10'])
# write EF.SMSP
if p.get('smsp'):
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
@@ -932,7 +784,7 @@ class SysmoUSIMSJS1(UsimCard):
data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
class FairwavesSIM(UsimCard):
class FairwavesSIM(Card):
"""
FairwavesSIM
@@ -1121,7 +973,7 @@ class OpenCellsSim(Card):
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
class WavemobileSim(UsimCard):
class WavemobileSim(Card):
"""
WavemobileSim
@@ -1212,7 +1064,7 @@ class WavemobileSim(UsimCard):
return None
class SysmoISIMSJA2(UsimCard, IsimCard):
class SysmoISIMSJA2(UsimCard):
"""
sysmocom sysmoISIM-SJA2
"""
@@ -1300,26 +1152,6 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
# EF.MSISDN
# TODO: Alpha Identifier (currently 'ff'O * 20)
# TODO: Capability/Configuration1 Record Identifier
# TODO: Extension1 Record Identifier
if p.get('msisdn') is not None:
msisdn = enc_msisdn(p['msisdn'])
content = 'ff' * 20 + msisdn + 'ff' * 2
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6F40', 1, content, force_len=True)
# EF.ACC
if p.get('acc'):
sw = self.update_acc(p['acc'])
if sw != '9000':
print("Programming ACC failed with code %s"%sw)
# Populate AIDs
self.read_aids()
# update EF-SIM_AUTH_KEY (and EF-USIM_AUTH_KEY_2G, which is
# hard linked to EF-USIM_AUTH_KEY)
self._scc.select_file(['3f00'])
@@ -1330,55 +1162,20 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
self._scc.update_binary('6f20', p['opc'], 17)
# update EF-USIM_AUTH_KEY in ADF.ISIM
if '9000' == self.select_adf_by_aid(adf="isim"):
self._scc.select_file(['3f00'])
aid = self.read_aid(isim = True)
if (aid):
self._scc.select_adf(aid)
if p.get('ki'):
self._scc.update_binary('af20', p['ki'], 1)
if p.get('opc'):
self._scc.update_binary('af20', p['opc'], 17)
# update EF.P-CSCF in ADF.ISIM
if self.file_exists(EF_ISIM_ADF_map['PCSCF']):
if p.get('pcscf'):
sw = self.update_pcscf(p['pcscf'])
else:
sw = self.update_pcscf("")
if sw != '9000':
print("Programming P-CSCF failed with code %s"%sw)
# update EF.DOMAIN in ADF.ISIM
if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
if p.get('ims_hdomain'):
sw = self.update_domain(domain=p['ims_hdomain'])
else:
sw = self.update_domain()
if sw != '9000':
print("Programming Home Network Domain Name failed with code %s"%sw)
# update EF.IMPI in ADF.ISIM
# TODO: Validate IMPI input
if self.file_exists(EF_ISIM_ADF_map['IMPI']):
if p.get('impi'):
sw = self.update_impi(p['impi'])
else:
sw = self.update_impi()
if sw != '9000':
print("Programming IMPI failed with code %s"%sw)
# update EF.IMPU in ADF.ISIM
# TODO: Validate IMPU input
# Support multiple IMPU if there is enough space
if self.file_exists(EF_ISIM_ADF_map['IMPU']):
if p.get('impu'):
sw = self.update_impu(p['impu'])
else:
sw = self.update_impu()
if sw != '9000':
print("Programming IMPU failed with code %s"%sw)
if '9000' == self.select_adf_by_aid():
self._scc.select_file(['3f00'])
aid = self.read_aid()
if (aid):
# update EF-USIM_AUTH_KEY in ADF.USIM
self._scc.select_adf(aid)
if p.get('ki'):
self._scc.update_binary('af20', p['ki'], 1)
if p.get('opc'):
@@ -1390,49 +1187,6 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
sw = self.update_ehplmn(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming EHPLMN failed with code %s"%sw)
# update EF.ePDGId in ADF.USIM
if self.file_exists(EF_USIM_ADF_map['ePDGId']):
if p.get('epdgid'):
sw = self.update_epdgid(p['epdgid'])
else:
sw = self.update_epdgid("")
if sw != '9000':
print("Programming ePDGId failed with code %s"%sw)
# update EF.ePDGSelection in ADF.USIM
if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
if p.get('epdgSelection'):
epdg_plmn = p['epdgSelection']
sw = self.update_ePDGSelection(epdg_plmn[:3], epdg_plmn[3:])
else:
sw = self.update_ePDGSelection("", "")
if sw != '9000':
print("Programming ePDGSelection failed with code %s"%sw)
# After successfully programming EF.ePDGId and EF.ePDGSelection,
# Set service 106 and 107 as available in EF.UST
# Disable service 95, 99, 115 if ISIM application is present
if self.file_exists(EF_USIM_ADF_map['UST']):
if p.get('epdgSelection') and p.get('epdgid'):
sw = self.update_ust(106, 1)
if sw != '9000':
print("Programming UST failed with code %s"%sw)
sw = self.update_ust(107, 1)
if sw != '9000':
print("Programming UST failed with code %s"%sw)
sw = self.update_ust(95, 0)
if sw != '9000':
print("Programming UST failed with code %s"%sw)
sw = self.update_ust(99, 0)
if sw != '9000':
print("Programming UST failed with code %s"%sw)
sw = self.update_ust(115, 0)
if sw != '9000':
print("Programming UST failed with code %s"%sw)
return

View File

@@ -26,7 +26,7 @@ from pySim.utils import rpad, b2h
class SimCardCommands(object):
def __init__(self, transport):
self._tp = transport
self._tp = transport;
self._cla_byte = "a0"
self.sel_ctrl = "0000"

View File

@@ -24,10 +24,10 @@
from smartcard.CardConnection import CardConnection
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException, CardConnectionException
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException
from smartcard.System import readers
from pySim.exceptions import NoCardError, ProtocolError
from pySim.exceptions import NoCardError
from pySim.transport import LinkBase
from pySim.utils import h2i, i2h
@@ -35,7 +35,7 @@ from pySim.utils import h2i, i2h
class PcscSimLink(LinkBase):
def __init__(self, reader_number=0):
r = readers()
r = readers();
self._reader = r[reader_number]
self._con = self._reader.createConnection()

View File

@@ -25,7 +25,6 @@ from __future__ import absolute_import
import serial
import time
import os.path
from pySim.exceptions import NoCardError, ProtocolError
from pySim.transport import LinkBase
@@ -35,8 +34,6 @@ from pySim.utils import h2b, b2h
class SerialSimLink(LinkBase):
def __init__(self, device='/dev/ttyUSB0', baudrate=9600, rst='-rts', debug=False):
if not os.path.exists(device):
raise ValueError("device file %s does not exist -- abort" % device)
self._sl = serial.Serial(
port = device,
parity = serial.PARITY_EVEN,
@@ -52,8 +49,7 @@ class SerialSimLink(LinkBase):
self._atr = None
def __del__(self):
if (hasattr(self, "_sl")):
self._sl.close()
self._sl.close()
def wait_for_card(self, timeout=None, newcardonly=False):
# Direct try
@@ -121,7 +117,7 @@ class SerialSimLink(LinkBase):
rst_meth = rst_meth_map[self._rst_pin[1:]]
rst_val = rst_val_map[self._rst_pin[0]]
except:
raise ValueError('Invalid reset pin %s' % self._rst_pin)
raise ValueError('Invalid reset pin %s' % self._rst_pin);
rst_meth(rst_val)
time.sleep(0.1) # 100 ms
@@ -132,7 +128,7 @@ class SerialSimLink(LinkBase):
if not b:
return 0
if ord(b) != 0x3b:
return -1
return -1;
self._dbg_print("TS: 0x%x Direct convention" % ord(b))
while ord(b) == 0x3b:
@@ -226,7 +222,7 @@ class SerialSimLink(LinkBase):
if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
continue
if not b:
break
break;
data += b
# Split datafield from SW

View File

@@ -135,23 +135,6 @@ EF_UST_map = {
109: 'MCPTT',
110: 'ePDG configuration Information for Emergency Service support',
111: 'ePDG configuration Information for Emergency Service configured',
112: 'eCall Data over IMS',
113: 'URI support for SMS-PP DOWNLOAD as defined in 3GPP TS 31.111 [12]',
114: 'From Preferred',
115: 'IMS configuration data',
116: 'TV configuration',
117: '3GPP PS Data Off',
118: '3GPP PS Data Off Service List',
119: 'V2X',
120: 'XCAP Configuration Data',
121: 'EARFCN list for MTC/NB-IOT UEs',
122: '5GS Mobility Management Information',
123: '5G Security Parameters',
124: 'Subscription identifier privacy support',
125: 'SUCI calculation by the USIM',
126: 'UAC Access Identities support',
127: 'Expect control plane-based Steering of Roaming information during initial registration in VPLMN',
128: 'Call control on PDU Session by USIM',
}
LOCI_STATUS_map = {
@@ -160,7 +143,6 @@ LOCI_STATUS_map = {
2: 'plmn not allowed',
3: 'locatation area not allowed'
}
EF_USIM_ADF_map = {
'LI': '6F05',
'ARR': '6F06',

View File

@@ -42,28 +42,5 @@ EF_IST_map = {
16: 'URI support for SMS-PP DOWNLOAD as defined in 3GPP TS 31.111 [31]',
17: 'From Preferred',
18: 'IMS configuration data',
19: 'XCAP Configuration Data',
20: 'WebRTC URI',
}
EF_ISIM_ADF_map = {
'IST': '6F07',
'IMPI': '6F02',
'DOMAIN': '6F03',
'IMPU': '6F04',
'AD': '6FAD',
'ARR': '6F06',
'PCSCF': '6F09',
'GBAP': '6FD5',
'GBANL': '6FD7',
'NAFKCA': '6FDD',
'UICCIARI': '6FE7',
'SMS': '6F3C',
'SMSS': '6F43',
'SMSR': '6F47',
'SMSP': '6F42',
'FromPreferred': '6FF7',
'IMSConfigData': '6FF8',
'XCAPConfigData': '6FFC',
'WebRTCURI': '6FFA'
19: 'XCAP Configuration Data'
}

View File

@@ -313,14 +313,4 @@ EF_SST_map = {
57: 'Multimedia Messaging Service (MMS)',
58: 'Extension 8',
59: 'MMS User Connectivity Parameters',
}
# 10.3.18 "EF.AD (Administrative data) "
EF_AD_mode_map = {
'00' : 'normal operation',
'80' : 'type approval operations',
'01' : 'normal operation + specific facilities',
'81' : 'type approval operations + specific facilities',
'02' : 'maintenance (off line)',
'04' : 'cell test operation',
}
}

View File

@@ -122,8 +122,11 @@ def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
if oplmn_disp: byte1 = byte1|0x02
return i2h([byte1])+s2h(name)
def hexstr_to_Nbytearr(s, nbytes):
return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
def hexstr_to_fivebytearr(s):
return [s[i:i+10] for i in range(0, len(s), 10) ]
def hexstr_to_threebytearr(s):
return [s[i:i+6] for i in range(0, len(s), 6) ]
# Accepts hex string representing three bytes
def dec_mcc_from_plmn(plmn):
@@ -137,9 +140,9 @@ def dec_mcc_from_plmn(plmn):
def dec_mnc_from_plmn(plmn):
ia = h2i(plmn)
digit1 = (ia[1] & 0xF0) >>4 # 2nd byte, MSB
digit2 = ia[2] & 0x0F # 3rd byte, LSB
digit3 = (ia[2] & 0xF0) >> 4 # 3nd byte, MSB
digit1 = ia[2] & 0x0F # 3rd byte, LSB
digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
return 0xFFF # 4095
return derive_mnc(digit1, digit2, digit3)
@@ -174,7 +177,7 @@ def dec_xplmn_w_act(fivehexbytes):
def format_xplmn_w_act(hexstr):
s = ""
for rec_data in hexstr_to_Nbytearr(hexstr, 5):
for rec_data in hexstr_to_fivebytearr(hexstr):
rec_info = dec_xplmn_w_act(rec_data)
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
rec_str = "unused"
@@ -223,7 +226,7 @@ def dec_xplmn(threehexbytes):
def format_xplmn(hexstr):
s = ""
for rec_data in hexstr_to_Nbytearr(hexstr, 3):
for rec_data in hexstr_to_threebytearr(hexstr):
rec_info = dec_xplmn(rec_data)
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
rec_str = "unused"
@@ -249,7 +252,7 @@ def calculate_luhn(cc):
"""
Calculate Luhn checksum used in e.g. ICCID and IMEI
"""
num = list(map(int, str(cc)))
num = map(int, str(cc))
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
@@ -457,62 +460,19 @@ def TLV_parser(bytelist):
bytelist = bytelist[ L+2 : ]
return ret
def enc_st(st, service, state=1):
def dec_epdgid(hexstr):
"""
Encodes the EF S/U/IST/EST and returns the updated Service Table
Parameters:
st - Current value of SIM/USIM/ISIM Service Table
service - Service Number to encode as activated/de-activated
state - 1 mean activate, 0 means de-activate
Returns:
s - Modified value of SIM/USIM/ISIM Service Table
Default values:
- state: 1 - Sets the particular Service bit to 1
"""
st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
s = ""
# Check whether the requested service is present in each byte
for i in range(0, len(st_bytes)):
# Byte i contains info about Services num (8i+1) to num (8i+8)
if service in range((8*i) + 1, (8*i) + 9):
byte = int(st_bytes[i], 16)
# Services in each byte are in order MSB to LSB
# MSB - Service (8i+8)
# LSB - Service (8i+1)
mod_byte = 0x00
# Copy bit by bit contents of byte to mod_byte with modified bit
# for requested service
for j in range(1, 9):
mod_byte = mod_byte >> 1
if service == (8*i) + j:
mod_byte = state == 1 and mod_byte|0x80 or mod_byte&0x7f
else:
mod_byte = byte&0x01 == 0x01 and mod_byte|0x80 or mod_byte&0x7f
byte = byte >> 1
s += ('%02x' % (mod_byte))
else:
s += st_bytes[i]
return s
def dec_addr_tlv(hexstr):
"""
Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.8, 4.2.102 and 4.2.104.
Decode ePDG Id to get EF.ePDGId or EF.ePDGIdEm.
See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
"""
# Convert from hex str to int bytes list
addr_tlv_bytes = h2i(hexstr)
epdgid_bytes = h2i(hexstr)
s = ""
# Get list of tuples containing parsed TLVs
tlvs = TLV_parser(addr_tlv_bytes)
tlvs = TLV_parser(epdgid_bytes)
for tlv in tlvs:
# tlv = (T, L, [V])
@@ -530,45 +490,29 @@ def dec_addr_tlv(hexstr):
# First byte in the value has the address type
addr_type = tlv[2][0]
# TODO: Support parsing of IPv6
# Address Type: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), other (Reserved)
# TODO: Support parsing of IPv4 and IPv6
if addr_type == 0x00: #FQDN
# Skip address tye byte i.e. first byte in value list
content = tlv[2][1:]
s += "\t%s # %s\n" % (i2h(content), i2s(content))
elif addr_type == 0x01: #IPv4
# Skip address tye byte i.e. first byte in value list
# Skip the unused byte in Octect 4 after address type byte as per 3GPP TS 31.102
ipv4 = tlv[2][2:]
content = '.'.join(str(x) for x in ipv4)
s += "\t%s # %s\n" % (i2h(ipv4), content)
return s
def enc_addr_tlv(addr, addr_type='00'):
def enc_epdgid(epdg_addr, addr_type='00'):
"""
Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.8, 4.2.102 and 4.2.104.
Encode ePDG Id so it can be stored to EF.ePDGId or EF.ePDGIdEm.
See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
Default values:
- addr_type: 00 - FQDN format of Address
- addr_type: 00 - FQDN format of ePDG Address
"""
s = ""
# TODO: Encoding of IPv6 address
if addr_type == '00': #FQDN
hex_str = s2h(addr)
# TODO: Encoding of IPv4 and IPv6 address
if addr_type == '00':
hex_str = s2h(epdg_addr)
s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
elif addr_type == '01': #IPv4
ipv4_list = addr.split('.')
ipv4_str = ""
for i in ipv4_list:
ipv4_str += ('%02x' % (int(i)))
# Unused bytes shall be set to 'ff'. i.e 4th Octet after Address Type is not used
# IPv4 Address is in octet 5 to octet 8 of the TLV data object
s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
return s
@@ -607,158 +551,21 @@ def init_reader(opts):
"""
Init card reader driver
"""
try:
if opts.pcsc_dev is not None:
print("Using PC/SC reader interface")
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
elif opts.osmocon_sock is not None:
print("Using Calypso-based (OsmocomBB) reader interface")
from pySim.transport.calypso import CalypsoSimLink
sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
elif opts.modem_dev is not None:
print("Using modem for Generic SIM Access (3GPP TS 27.007)")
from pySim.transport.modem_atcmd import ModemATCommandLink
sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
else: # Serial reader is default
print("Using serial reader interface")
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
return sl
except Exception as e:
print("Card reader initialization failed with exception:\n" + str(e))
return None
if opts.pcsc_dev is not None:
print("Using PC/SC reader interface")
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
elif opts.osmocon_sock is not None:
print("Using Calypso-based (OsmocomBB) reader interface")
from pySim.transport.calypso import CalypsoSimLink
sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
elif opts.modem_dev is not None:
print("Using modem for Generic SIM Access (3GPP TS 27.007)")
from pySim.transport.modem_atcmd import ModemATCommandLink
sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
else: # Serial reader is default
print("Using serial reader interface")
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
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.
See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
Default values:
- epdg_priority: '0001' - 1st Priority
- epdg_fqdn_format: '00' - Operator Identifier FQDN
"""
plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
# TODO: Handle encoding of Length field for length more than 127 Bytes
content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
content = rpad(content, len(hexstr))
return content
def dec_ePDGSelection(sixhexbytes):
"""
Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
"""
res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
plmn_chars = 6
epdg_priority_chars = 4
epdg_fqdn_format_chars = 2
# first three bytes (six ascii hex chars)
plmn_str = sixhexbytes[:plmn_chars]
# two bytes after first three bytes
epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
# 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]
res['mcc'] = dec_mcc_from_plmn(plmn_str)
res['mnc'] = dec_mnc_from_plmn(plmn_str)
res['epdg_priority'] = epdg_priority_str
res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
return res
def format_ePDGSelection(hexstr):
ePDGSelection_info_tag_chars = 2
ePDGSelection_info_tag_str = hexstr[:2]
s = ""
# Minimum length
len_chars = 2
# TODO: Need to determine length properly - definite length support only
# Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
# As per spec, length is 5n, n - number of PLMNs
# But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
# Totalling to 6 Bytes, maybe length should be 6n
len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
# Not programmed scenario
if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
len_chars = 0
ePDGSelection_info_tag_chars = 0
if len_str[0] == '8':
# The bits 7 to 1 denotes the number of length octets if length > 127
if int(len_str[1]) > 0:
# Update number of length octets
len_chars = len_chars * int(len_str[1])
len_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
content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
for rec_data in hexstr_to_Nbytearr(content_str, 6):
rec_info = dec_ePDGSelection(rec_data)
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
rec_str = "unused"
else:
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'])
s += "\t%s # %s\n" % (rec_data, rec_str)
return s
def get_addr_type(addr):
"""
Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
Return: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), None (Bad address argument given)
TODO: Handle IPv6
"""
# Empty address string
if not len(addr):
return None
import sys
# Handle python3 and python2 - unicode
if sys.version_info[0] < 3:
addr_str = unicode(addr)
else:
addr_str = addr
addr_list = addr.split('.')
# Check for IPv4/IPv6
try:
import ipaddress
# Throws ValueError if addr is not correct
ipa = ipaddress.ip_address(addr_str)
if ipa.version == 4:
return 0x01
elif ipa.version == 6:
return 0x02
except Exception as e:
invalid_ipv4 = True
for i in addr_list:
# Invalid IPv4 may qualify for a valid FQDN, so make check here
# e.g. 172.24.15.300
import re
if not re.match('^[0-9_]+$', i):
invalid_ipv4 = False
break
if invalid_ipv4:
return None
fqdn_flag = True
for i in addr_list:
# Only Alpha-numeric characters and hyphen - RFC 1035
import re
if not re.match("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?$", i):
fqdn_flag = False
break
# FQDN
if fqdn_flag:
return 0x00
return None
return sl

View File

@@ -12,7 +12,7 @@ class DecTestCase(unittest.TestCase):
"ffffff0002",
"ffffff0001",
]
self.assertEqual(utils.hexstr_to_Nbytearr(input_str, 5), expected)
self.assertEqual(utils.hexstr_to_fivebytearr(input_str), expected)
def testDecMCCfromPLMN(self):
self.assertEqual(utils.dec_mcc_from_plmn("92f501"), 295)

View File

@@ -42,9 +42,7 @@ HPLMNAcT:
ACC: 0008
MSISDN: Not available
Administrative data: 00000002
MS operation mode: normal operation
Ciphering Indicator: disabled
AD: 00000002
SIM Service Table: ff3cc3ff030fff0f000fff03f0c0
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
@@ -116,5 +114,6 @@ USIM Service Table: 01ea1ffc21360480010000
Service 64 - VGCS security
Service 65 - VBS security
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -49,9 +49,7 @@ OPLMNwAcT:
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 6a82.
ACC: abce
MSISDN: Not available
Administrative data: 00ffff02
MS operation mode: normal operation
Ciphering Indicator: enabled
AD: 00ffff02
SIM Service Table: ff33ff0f3c00ff0f000cf0c0f0030000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
@@ -132,5 +130,6 @@ USIM Service Table: 9eff1b3c37fe5900000000
Service 53 - Extension 8
Service 55 - MMS User Connectivity Parameters
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -16,9 +16,7 @@ OPLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
ACC: ffff
MSISDN: Not available
Administrative data: 000000
MS operation mode: normal operation
Ciphering Indicator: disabled
AD: 000000
SIM Service Table: ff3fff0f0300f003000c
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)

View File

@@ -4,4 +4,4 @@ ICCID=1122334455667788990
KI=AABBCCDDEEFFAABBCCDDEEFFAABBCCDD
OPC=12345678901234567890123456789012
IMSI=001010000000102
ADM=11111111
ADM=72273953

View File

@@ -1,7 +1,7 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmoISIM-SJA2
ICCID: 8988211900000000025
ICCID: 8988211900000000004
IMSI: 001010000000102
GID1: ffffffffffffffffffff
GID2: ffffffffffffffffffff
@@ -52,11 +52,9 @@ HPLMNAcT:
ffffff0000 # unused
ffffff0000 # unused
ACC: 0200
MSISDN (NPI=1 ToN=3): 6766266
Administrative data: 00000002
MS operation mode: normal operation
Ciphering Indicator: disabled
ACC: 0001
MSISDN (NPI=1 ToN=1): +1234
AD: 00000002
SIM Service Table: ff33ffff3f003f0f300cf0c3f00000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
@@ -103,12 +101,12 @@ SIM Service Table: ff33ffff3f003f0f300cf0c3f00000
Service 59 - MMS User Connectivity Parameters
EHPLMN:
00f110 # MCC: 001 MNC: 001
ffffff # unused
ffffff # unused
ffffff # unused
00f110 # MCC: 001 MNC: 001
ffffff # unused
ffffff # unused
ffffff # unused
USIM Service Table: beff9f9de73e0408400170330006002e00000000
USIM Service Table: beff9f9de73e0408400170730000002e00000000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
@@ -156,54 +154,11 @@ USIM Service Table: beff9f9de73e0408400170330006002e00000000
Service 90 - Operator CSG Lists and corresponding indications
Service 93 - Communication Control for IMS by USIM
Service 94 - Extended Terminal Applications
Service 106 - ePDG configuration Information support
Service 107 - ePDG configuration Information configured
Service 122 - 5GS Mobility Management Information
Service 123 - 5G Security Parameters
Service 124 - Subscription identifier privacy support
Service 126 - UAC Access Identities support
Service 95 - Support of UICC access to IMS
ePDGId:
Not available
ePDGSelection:
ffffffffffff # unused
ffffffffffff # unused
ffffffffffff # unused
ffffffffffff # unused
P-CSCF:
Not available
Not available
Not available
Not available
Not available
Not available
Not available
Not available
Home Network Domain Name: Not available
IMS private user identity: Not available
IMS public user identity:
Not available
Not available
Not available
Not available
Not available
Not available
Not available
Not available
UICC IARI:
Not available
Not available
Not available
Not available
Not available
Not available
Not available
Not available
ISIM Service Table: 190200
Service 1 - P-CSCF address
Service 4 - GBA-based Local Key Establishment Mechanism

View File

@@ -54,9 +54,7 @@ HPLMNAcT:
ACC: 0008
MSISDN (NPI=1 ToN=1): +77776336143
Administrative data: 00000002
MS operation mode: normal operation
Ciphering Indicator: disabled
AD: 00000002
SIM Service Table: ff3fffff3f003f1ff00c00c0f00000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
@@ -140,5 +138,6 @@ USIM Service Table: 9e6b1dfc67f6580000
Service 53 - Extension 8
Service 55 - MMS User Connectivity Parameters
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -16,9 +16,7 @@ OPLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
ACC: 0008
MSISDN: Not available
Administrative data: 000000
MS operation mode: normal operation
Ciphering Indicator: disabled
AD: 000000
SIM Service Table: ff3fff0f0f0000030000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)

13
script_format.py Normal file
View File

@@ -0,0 +1,13 @@
class ScriptFormat():
def parse_process(self, text, stmt_cb, stmt_cb_kwargs={}):
p = self.parse_xform(text)
#print(p.pretty())
for stmt in p.children:
stmt_cb(stmt, **stmt_cb_kwargs)
def parse_process_file(self, fname, stmt_cb, stmt_cb_kwargs={}):
f = open(fname, "r")
text = f.read()
return self.parse_process(text, stmt_cb, stmt_cb_kwargs)