15 Commits

Author SHA1 Message Date
Alexander Chemeris
5dfda9fdd7 pySim-fairwaves-prog: Read a selected A3/A8 algorithm.
Change-Id: I757ea725bd5616dbd6ef329ea5981063fd780761
2018-01-27 02:09:04 +09:00
Alexander Chemeris
99b5e321e5 Checking in pySim-read-all.py - utility to read all known files on a SIM card.
Change-Id: Iaa301a8a6356d610753c2bba286187c3220833a9
2018-01-27 02:08:25 +09:00
Alexander Chemeris
d21ef12f8d commands: Allow reading binary/record files if they are already selected.
Change-Id: Iad3b240eab8a54a4d0ef2623bc9dd17f7dce5fa5
2018-01-27 02:07:10 +09:00
Alexander Chemeris
1198ad9e15 utils: Add a function to decode SELECT result for EF files.
Change-Id: Ib2af7fe5205654c5277fec01b6b0b10a68e472b9
2018-01-27 02:06:30 +09:00
Alexander Chemeris
cc85a1ee71 utils: Zero-pad MCC and MNC in enc_plmn()
Change-Id: I94607f6a87a416d05bd577a29d1466452921d604
2018-01-27 02:05:20 +09:00
Alexander Chemeris
2322c1f9ff Checking in various utilities to manipulate Fairwaves SIM card DB.
Change-Id: Ibb49b127df68fd210e6b75e4f0b9c667d2c2cc7c
2018-01-26 16:45:59 +09:00
Alexander Chemeris
6a38c4a2f3 Checking in pySim-run-gsm.py - utility to run A3/A8 on a SIM card.
Change-Id: Iea8f61bc86499734d432f668dd0b87af999abf27
2018-01-26 16:24:27 +09:00
Alexander Chemeris
575f64e38a pySim-read: Read SPN and use new EF constants to address other files.
Change-Id: I3207edaa9670d1c76b1ba2142f1042794055e94c
2018-01-26 15:57:19 +09:00
Alexander Chemeris
4d5c0293a2 pySim-fairwaves-prog: Write Ki/OPC/IMSI as read from the input file.
Commenting out generation of random ones.

Change-Id: I36a87f37671cd64d801006d7e2def8f41e95700a
2018-01-26 15:55:58 +09:00
Alexander Chemeris
3a27424ff8 pySim-fairwaves-prog: Add an option to play sound on successful write in the batch mode.
Change-Id: I440a1fd30abfaf95da2daf7b618f6c25c3b092d4
2018-01-26 15:53:26 +09:00
Alexander Chemeris
5705837a1b ts_51_011: Fix a comment language.
Change-Id: I9e92ad180791223fc90a61ea1e68d18dd67cc000
2018-01-26 15:52:29 +09:00
Alexander Chemeris
b5208b5544 cards: Add ability to read/set A3/A8 algorithm for Fairwaves SIM cards.
Change-Id: Ibb68c2a695b1f97ca11e25c14770132cea604cbe
2018-01-26 15:51:19 +09:00
Alexander Chemeris
4dabfda193 cards: Minor formating cleanup
Change-Id: I0d1cf38a422c36b26dc125384d2df70e82e752e1
2018-01-26 15:50:46 +09:00
Alexander Chemeris
6d4a0a1a3e Add a comanda to run GSM auth algorithm.
Change-Id: I55d4cf5ad4d50c473ed4febb171cbc8854d1fa99
2018-01-26 15:49:49 +09:00
Alexander Chemeris
e6d4faa6f5 Checking in pySim-fairwaves-prog.py utility.
This utility is an example utility for programming Fairwaves SIM cards.
The original pySim-prog.py utility is already bloated with features
and is difficult to modify so we decided to create a leaner and easier to
maintain and modify version.

Change-Id: I9f58e1b45d1785d59cef161eab1388332a97936b
2018-01-11 13:27:08 +09:00
48 changed files with 1205 additions and 3841 deletions

View File

@@ -1,3 +0,0 @@
[gerrit]
host=gerrit.osmocom.org
project=pysim

View File

@@ -28,22 +28,6 @@ You can clone from the official libosmocore.git repository using
There is a cgit interface at <http://git.osmocom.org/pysim/>
Dependencies
------------
pysim requires:
- pyscard
- serial
- pytlv (for specific card types)
Example for Debian:
apt-get install python-pyscard python-serial python-pip
pip install pytlv
Mailing List
------------
@@ -101,7 +85,7 @@ sc = SimCardCommands(sl)
sl.wait_for_card()
# Print IMSI
print(sc.read_binary(['3f00', '7f20', '6f07']))
print sc.read_binary(['3f00', '7f20', '6f07'])
# Run A3/A8
print(sc.run_gsm('00112233445566778899aabbccddeeff'))
print sc.run_gsm('00112233445566778899aabbccddeeff')

View File

@@ -1,19 +0,0 @@
#!/bin/sh
set -e
if [ ! -d "./pysim-testdata/" ] ; then
echo "###############################################"
echo "Please call from pySim-prog top directory"
echo "###############################################"
exit 1
fi
virtualenv -p python2 venv --system-site-packages
. venv/bin/activate
pip install pytlv
pip install pyyaml
cd pysim-testdata
../tests/pysim-test.sh

View File

@@ -1,16 +0,0 @@
This file aims to describe the format of the CSV file pySim uses.
The first line contains the fieldnames which will be used by pySim. This
avoids having a specific order.
The field names are the following:
iccid: ICCID of the card. Used to identify the cards (with --read-iccid)
imsi: IMSI of the card
mcc: Mobile Country Code (optional)
mnc: Mobile Network Code (optional)
smsp: MSISDN of the SMSC (optional)
ki: Ki
opc: OPc
acc: Access class of the SIM (optional)
pin_adm: Admin PIN of the SIM. Needed to reprogram various files

View File

@@ -1,141 +0,0 @@
#!/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})

123
fairwaves_db_randomize.py Executable file
View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python
#
# Utility to randomize Ki and other values in a Fairwaves SIM card DB file
#
# Copyright (C) 2017-2018 Alexander Chemeris <alexander.chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from optparse import OptionParser
import os
import sys
import csv
import random
from pySim.utils import derive_milenage_opc
#from pySim.utils import h2b
def h2b(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def write_sim_db(filename, sim_db):
with open(filename, 'a') as f:
cw = csv.writer(f, delimiter=' ')
for iccid in sorted(sim_db.iterkeys()):
cw.writerow([x for x in sim_db[iccid]])
def process_sim(sim_keys, opts):
# Update IMSI
imsi = sim_keys[1]
imsi = "%03d%02d%s" % (opts.mcc, opts.mnc, imsi[5:])
sim_keys[1] = imsi
# Update Ki
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)]).upper()
sim_keys[8] = ki
# Update OPC
op_opc = derive_milenage_opc(ki, opts.op).upper()
sim_keys[9] = '01' + op_opc
return sim_keys
def process_db(sim_db, opts):
sim_db_new = {}
for iccid, sim_keys in sim_db.items():
sim_db_new[iccid] = process_sim(sim_keys, opts)
return sim_db_new
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to randomize Ki and other values in a Fairwaves SIM card DB file.")
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space separated)",
default="sim_db.dat",
)
parser.add_option("-o", "--out-db", dest="out_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to write keys to (space separated)",
default=None,
)
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default=001,
)
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default=01,
)
parser.add_option("--op", dest="op",
help="Set OP to derive OPC from OP and KI [default: %default]",
default='00000000000000000000000000000000',
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
if opts.out_db_filename is None:
print("Please specify output DB filename")
sys.exit(1)
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
sim_db = process_db(sim_db, opts)
print("Writing SIM DB ...")
write_sim_db(opts.out_db_filename, sim_db)

154
fairwaves_db_to_hlr.py Executable file
View File

@@ -0,0 +1,154 @@
#!/usr/bin/env python
#
# Utility to write data from a Fairwaves SIM card DB to Osmocom HLR DB
#
# Copyright (C) 2017-2018 Alexander Chemeris <alexander.chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from optparse import OptionParser
import os
import sys
import csv
#from pySim.utils import h2b
def h2b(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
# reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def _dbi_binary_quote(s):
# Count usage of each char
cnt = {}
for c in s:
cnt[c] = cnt.get(c, 0) + 1
# Find best offset
e = 0
m = len(s)
for i in range(1, 256):
if i == 39:
continue
sum_ = cnt.get(i, 0) + cnt.get((i+1)&0xff, 0) + cnt.get((i+39)&0xff, 0)
if sum_ < m:
m = sum_
e = i
if m == 0: # No overhead ? use this !
break;
# Generate output
out = []
out.append( chr(e) ) # Offset
for c in s:
x = (256 + ord(c) - e) % 256
if x in (0, 1, 39):
out.append('\x01')
out.append(chr(x+1))
else:
out.append(chr(x))
return ''.join(out)
def write_key_hlr(opts, sim_data):
# SQLite3 OpenBSC HLR
import sqlite3
conn = sqlite3.connect(opts.hlr_db_filename)
imsi = sim_data[1]
ki = sim_data[8]
c = conn.execute('SELECT id FROM Subscriber WHERE imsi = ?', (imsi,))
sub_id = c.fetchone()
if sub_id is None:
print("IMSI %s is not found in the HLR" % (imsi,))
return None
sub_id = sub_id[0]
print("IMSI %s has ID %d, writing Ki %s" % (imsi, sub_id, ki))
# c = conn.execute(
# 'INSERT INTO Subscriber ' +
# '(imsi, name, extension, authorized, created, updated) ' +
# 'VALUES ' +
# '(?,?,?,1,datetime(\'now\'),datetime(\'now\'));',
# [
# params['imsi'],
# params['name'],
# '9' + params['iccid'][-5:-1]
# ],
# )
# sub_id = c.lastrowid
# c.close()
c = conn.execute(
'INSERT OR REPLACE INTO AuthKeys ' +
'(subscriber_id, algorithm_id, a3a8_ki)' +
'VALUES ' +
'(?,?,?)',
[ sub_id, 2, sqlite3.Binary(_dbi_binary_quote(h2b(ki))) ],
)
c = conn.execute(
'DELETE FROM AuthLastTuples WHERE subscriber_id = ?',
[ sub_id ],
)
conn.commit()
conn.close()
return True
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to write data from a Fairwaves SIM card DB to Osmocom HLR DB.")
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space searated)",
default="sim_db.dat",
)
parser.add_option("-d", "--hlr", dest="hlr_db_filename", type='string', metavar="FILE",
help="filename of a HLR SQLite3 DB to write the keys to",
default="hlr.sqlite3",
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
for iccid, sim in sim_db.items():
write_key_hlr(opts, sim)

82
fairwaves_db_uniq.py Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python
#
# Utility to remove duplicates from a Fairwaves SIM card DB file
#
# Copyright (C) 2017-2018 Alexander Chemeris <alexander.chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from optparse import OptionParser
import os
import sys
import csv
#from pySim.utils import h2b
def h2b(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
# reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def write_sim_db(filename, sim_db):
with open(filename, 'a') as f:
cw = csv.writer(f, delimiter=' ')
for iccid in sorted(sim_db.iterkeys()):
cw.writerow([x for x in sim_db[iccid]])
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to remove duplicates from a Fairwaves SIM card DB file")
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space separated)",
default="sim_db.dat",
)
parser.add_option("-o", "--out-db", dest="out_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to write keys to (space separated)",
default=None,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
if opts.out_db_filename is None:
print("Please specify output DB filename")
sys.exit(1)
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
print("Writing SIM DB ...")
write_sim_db(opts.out_db_filename, sim_db)

View File

@@ -1,55 +0,0 @@
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

View File

@@ -1,74 +0,0 @@
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

View File

@@ -1,84 +0,0 @@
#!/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)

289
pySim-fairwaves-prog.py Executable file
View File

@@ -0,0 +1,289 @@
#!/usr/bin/env python
#
# Utility to update SPN field of a SIM card
#
# Copyright (C) 2017-2018 Alexander Chemeris <alexander.chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from optparse import OptionParser
import os
import sys
import csv
import random
import subprocess
from pySim.commands import SimCardCommands
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, derive_milenage_opc
from pySim.cards import card_autodetect
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def write_params_csv(filename, sim_keys):
with open(filename, 'a') as f:
cw = csv.writer(f, delimiter=' ')
cw.writerow([x for x in sim_keys])
def program_sim_card(card, sim_db, opts):
# Program the card
print("Reading SIM card ...")
# EF.ICCID
(iccid, sw) = card.read_iccid()
if sw != '9000':
print("ICCID: Can't read, response code = %s" % (sw,))
sys.exit(1)
print("ICCID: %s" % (iccid))
# Find SIM card keys in the DB
sim_keys = sim_db.get(iccid+'F')
if sim_keys == None:
print("Can't find SIM card in the SIM DB.")
sys.exit(1)
# EF.IMSI
(imsi, sw) = card.read_imsi()
if sw != '9000':
print("IMSI: Can't read, response code = %s" % (sw,))
sys.exit(1)
print("IMSI: %s" % (imsi))
# EF.SPN
((name, hplmn_disp, oplmn_disp), sw) = card.read_spn()
if sw == '9000':
print("Service Provider Name: %s" % name)
print(" display for HPLMN %s" % hplmn_disp)
print(" display for other PLMN %s" % oplmn_disp)
else:
print("Old SPN: Can't read, response code = %s" % (sw,))
print("Entring ADM code...")
# Enter ADM code to get access to proprietary files
sw = card.verify_adm(h2b(sim_keys[6]))
if sw != '9000':
print("Fail to verify ADM code with result = %s" % (sw,))
sys.exit(1)
# Read EF.Ki
(ki, sw) = card.read_ki()
if sw == '9000':
ki = ki.upper()
print("Ki: %s" % ki)
else:
print("Ki: Can't read, response code = %s" % (sw,))
# Read EF.OP/OPC
((op_opc_type, op_opc), sw) = card.read_op_opc()
if sw == '9000':
op_opc = op_opc.upper()
print("%s: %s" % (op_opc_type, op_opc))
else:
print("Ki: Can't read, response code = %s" % (sw,))
# Read EF.A3A8
(a3a8, sw) = card.read_a3a8()
if sw == '9000':
print("A3/A8: %s" % (a3a8,))
else:
print("A3/A8: Can't read, response code = %s" % (sw,))
print("Programming...")
# Update SPN
sw = card.update_spn(opts.name, False, False)
if sw != '9000':
print("SPN: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update Ki
ki = sim_keys[8]
# ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)]).upper()
# sim_keys[8] = ki
sw = card.update_ki(sim_keys[8])
if sw != '9000':
print("Ki: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update OPC
op_opc = sim_keys[9][2:]
# op_opc = derive_milenage_opc(ki, opts.op).upper()
# sim_keys[9] = '01' + op_opc
sw = card.update_opc(sim_keys[9][2:])
if sw != '9000':
print("OPC: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update Home PLMN
sw = card.update_hplmn_act(opts.mcc, opts.mnc)
if sw != '9000':
print("MCC/MNC: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update IMSI
imsi = sim_keys[1]
# imsi = "%03d%02d%s" % (opts.mcc, opts.mnc, imsi[5:])
# sim_keys[1] = imsi
sw = card.update_imsi(imsi)
if sw != '9000':
print("IMSI: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Verify EF.IMSI
(imsi_new, sw) = card.read_imsi()
if sw != '9000':
print("IMSI: Can't read, response code = %s" % (sw,))
sys.exit(1)
print("IMSI: %s" % (imsi_new))
# Verify EF.SPN
((name, hplmn_disp, oplmn_disp), sw) = card.read_spn()
if sw == '9000':
print("Service Provider Name: %s" % name)
print(" display for HPLMN %s" % hplmn_disp)
print(" display for other PLMN %s" % oplmn_disp)
else:
print("New SPN: Can't read, response code = %s" % (sw,))
# Verify EF.Ki
(ki_new, sw) = card.read_ki()
if sw == '9000':
ki_new = ki_new.upper()
print("Ki: %s (%s)" % (ki_new, "match" if (ki==ki_new) else ("DON'T match %s" % ki)))
else:
print("New Ki: Can't read, response code = %s" % (sw,))
# Verify EF.OP/OPC
((op_opc_type_new, op_opc_new), sw) = card.read_op_opc()
if sw == '9000':
op_opc_new = op_opc_new.upper()
print("%s: %s (%s)" % (op_opc_type_new, op_opc_new, "match" if (op_opc==op_opc_new) else ("DON'T match %s" % op_opc)))
else:
print("Ki: Can't read, response code = %s" % (sw,))
# Done with this card
print "Done !\n"
return sim_keys
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="An example utility to program Fairwaves SIM cards."
" Modify it to your own specific needs.")
parser.add_option("-d", "--device", dest="device", metavar="DEV",
help="Serial Device for SIM access [default: %default]",
default="/dev/ttyUSB0",
)
parser.add_option("-b", "--baud", dest="baudrate", type="int", metavar="BAUD",
help="Baudrate used for SIM access [default: %default]",
default=9600,
)
parser.add_option("-p", "--pcsc-device", dest="pcsc_dev", type='int', metavar="PCSC",
help="Which PC/SC reader number for SIM access",
default=None,
)
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space searated)",
default="sim_db.dat",
)
parser.add_option("-o", "--out-db", dest="out_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to write keys to (space searated)",
default="out.csv",
)
parser.add_option("--batch", dest="batch",
help="Process SIM cards in batch mode - don't exit after programming and wait for the next SIM card to be inserted.",
default=False, action="store_true",
)
parser.add_option("--sound", dest="sound_file", type='string', metavar="SOUND_FILE",
help="Only in the batch mode. Play the given sound file on successful SIM programming",
)
parser.add_option("-n", "--name", dest="name",
help="Operator name [default: %default]",
default="Fairwaves",
)
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default=001,
)
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default=01,
)
parser.add_option("--op", dest="op",
help="Set OP to derive OPC from OP and KI [default: %default]",
default='00000000000000000000000000000000',
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
if opts.batch:
print("Batch mode enabled! Press Ctrl-C to exit")
# Loop once in non-batch mode and loop forever in batch mode
first_run = True
while first_run or opts.batch:
print("Insert a SIM card to program...")
sl.wait_for_card(newcardonly=not first_run)
first_run = False
card = card_autodetect(scc)
if card is None:
print("Card autodetect failed")
continue
print "Autodetected card type %s" % card.name
sim_keys = program_sim_card(card, sim_db, opts)
write_params_csv(opts.out_db_filename, sim_keys)
if opts.sound_file is not None and opts.sound_file != "":
subprocess.call(["paplay", opts.sound_file])

View File

@@ -30,7 +30,6 @@ import os
import random
import re
import sys
import traceback
try:
import json
@@ -39,11 +38,9 @@ except ImportError:
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.cards import _cards_classes, card_detect
from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn, dec_iccid, init_reader
from pySim.ts_51_011 import EF
from pySim.card_handler import *
from pySim.utils import *
from pySim.cards import _cards_classes
from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn
def parse_options():
@@ -61,32 +58,13 @@ def parse_options():
help="Which PC/SC reader number for SIM access",
default=None,
)
parser.add_option("--modem-device", dest="modem_dev", metavar="DEV",
help="Serial port of modem for Generic SIM Access (3GPP TS 27.007)",
default=None,
)
parser.add_option("--modem-baud", dest="modem_baud", type="int", metavar="BAUD",
help="Baudrate used for modem's port [default: %default]",
default=115200,
)
parser.add_option("--osmocon", dest="osmocon_sock", metavar="PATH",
help="Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)",
default=None,
)
parser.add_option("-t", "--type", dest="type",
help="Card type (user -t list to view) [default: %default]",
default="auto",
)
parser.add_option("-T", "--probe", dest="probe",
help="Determine card type",
default=False, action="store_true"
)
parser.add_option("-a", "--pin-adm", dest="pin_adm",
help="ADM PIN used for provisioning (overwrites default)",
)
parser.add_option("-A", "--pin-adm-hex", dest="pin_adm_hex",
help="ADM PIN used for provisioning, as hex string (16 characters long",
)
parser.add_option("-e", "--erase", dest="erase", action='store_true',
help="Erase beforehand [default: %default]",
default=False,
@@ -106,21 +84,16 @@ def parse_options():
help="Country code [default: %default]",
default=1,
)
parser.add_option("-x", "--mcc", dest="mcc", type="string",
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default="901",
default=901,
)
parser.add_option("-y", "--mnc", dest="mnc", type="string",
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default="55",
)
parser.add_option("--mnclen", dest="mnclen", type="choice",
help="Length of Mobile Network Code [default: %default]",
default=2,
choices=[2, 3],
default=55,
)
parser.add_option("-m", "--smsc", dest="smsc",
help="SMSC number (Start with + for international no.) [default: '00 + country code + 5555']",
help="SMSP [default: '00 + country code + 5555']",
)
parser.add_option("-M", "--smsp", dest="smsp",
help="Raw SMSP content in hex [default: auto from SMSC]",
@@ -132,9 +105,6 @@ def parse_options():
parser.add_option("-i", "--imsi", dest="imsi",
help="International Mobile Subscriber Identity",
)
parser.add_option("--msisdn", dest="msisdn",
help="Mobile Subscriber Integrated Services Digital Number",
)
parser.add_option("-k", "--ki", dest="ki",
help="Ki (default is to randomize)",
)
@@ -146,13 +116,10 @@ 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("--read-imsi", dest="read_imsi", action="store_true",
help="Read the IMSI from the CARD", default=False
)
parser.add_option("--read-iccid", dest="read_iccid", action="store_true",
help="Read the ICCID from the CARD", default=False
)
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
help="Secret used for ICCID/IMSI autogen",
)
@@ -181,22 +148,17 @@ def parse_options():
parser.add_option("--dry-run", dest="dry_run",
help="Perform a 'dry run', don't actually program the card",
default=False, action="store_true")
parser.add_option("--card_handler", dest="card_handler", metavar="FILE",
help="Use automatic card handling machine")
(options, args) = parser.parse_args()
if options.type == 'list':
for kls in _cards_classes:
print(kls.name)
print kls.name
sys.exit(0)
if options.probe:
return options
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):
parser.error("CSV mode needs either an IMSI, --read-imsi, --read-iccid or batch mode")
if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False):
parser.error("CSV mode needs either an IMSI, --read-imsi or batch mode")
if options.read_csv is None:
parser.error("CSV mode requires a CSV input file")
elif options.source == 'cmdline':
@@ -227,7 +189,7 @@ def _digits(secret, usage, len, num):
return d[0:len]
def _mcc_mnc_digits(mcc, mnc):
return '%s%s' % (mcc, mnc)
return ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
def _cc_digits(cc):
return ('%03d' if cc > 100 else '%02d') % cc
@@ -280,17 +242,8 @@ def gen_parameters(opts):
mcc = opts.mcc
mnc = opts.mnc
if not mcc.isdigit() or not mnc.isdigit():
raise ValueError('mcc & mnc must only contain decimal digits')
if len(mcc) < 1 or len(mcc) > 3:
raise ValueError('mcc must be between 1 .. 3 digits')
if len(mnc) < 1 or len(mnc) > 3:
raise ValueError('mnc must be between 1 .. 3 digits')
# MCC always has 3 digits
mcc = lpad(mcc, 3, "0")
# MNC must be at least 2 digits
mnc = lpad(mnc, 2, "0")
if not ((0 < mcc < 999) and (0 < mnc < 999)):
raise ValueError('mcc & mnc must be between 0 and 999')
# Digitize country code (2 or 3 digits)
cc_digits = _cc_digits(opts.country)
@@ -298,26 +251,11 @@ def gen_parameters(opts):
# Digitize MCC/MNC (5 or 6 digits)
plmn_digits = _mcc_mnc_digits(mcc, mnc)
if opts.name is not None:
if len(opts.name) > 16:
raise ValueError('Service Provider Name must max 16 characters!');
if opts.msisdn is not None:
msisdn = opts.msisdn
if msisdn[0] == '+':
msisdn = msisdn[1:]
if not msisdn.isdigit():
raise ValueError('MSISDN must be digits only! '
'Start with \'+\' for international numbers.')
if len(msisdn) > 10 * 2:
# TODO: Support MSISDN of length > 20 (10 Bytes)
raise ValueError('MSISDNs longer than 20 digits are not (yet) supported.')
# ICCID (19 digits, E.118), though some phase1 vendors use 20 :(
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 !');
if not _isnum(iccid, 19):
raise ValueError('ICCID must be 19 digits !');
else:
if opts.num is None:
@@ -326,7 +264,7 @@ def gen_parameters(opts):
iccid = (
'89' + # Common prefix (telecom)
cc_digits + # Country Code on 2/3 digits
plmn_digits # MCC/MNC on 5/6 digits
plmn_digits # MCC/MNC on 5/6 digits
)
ml = 18 - len(iccid)
@@ -374,19 +312,14 @@ def gen_parameters(opts):
raise ValueError('SMSP must be at least 28 bytes')
else:
ton = "81"
if opts.smsc is not None:
smsc = opts.smsc
if smsc[0] == '+':
ton = "91"
smsc = smsc[1:]
if not _isnum(smsc):
raise ValueError('SMSC must be digits only!\n \
Start with \'+\' for international numbers')
raise ValueError('SMSC must be digits only !')
else:
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,) + "81" + swap_nibbles(rpad(smsc, 20))
smsp = (
'e1' + # Parameters indicator
@@ -427,7 +360,13 @@ def gen_parameters(opts):
else:
opc = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
pin_adm = sanitize_pin_adm(opts)
if opts.pin_adm is not None:
pin_adm = opts.pin_adm
if not re.match('^([0-9a-fA-F][0-9a-fA-F])+$', pin_adm):
raise ValueError('ADM pin needs to be in hex format (even number of hex digits)')
else:
pin_adm = None
# Return that
return {
@@ -441,26 +380,21 @@ def gen_parameters(opts):
'opc' : opc,
'acc' : acc,
'pin_adm' : pin_adm,
'msisdn' : opts.msisdn,
}
def print_parameters(params):
s = ["Generated card parameters :"]
if 'name' in params:
s.append(" > Name : %(name)s")
if 'smsp' in params:
s.append(" > SMSP : %(smsp)s")
s.append(" > ICCID : %(iccid)s")
s.append(" > MCC/MNC : %(mcc)s/%(mnc)s")
s.append(" > IMSI : %(imsi)s")
s.append(" > Ki : %(ki)s")
s.append(" > OPC : %(opc)s")
if 'acc' in params:
s.append(" > ACC : %(acc)s")
s.append(" > ADM1(hex): %(pin_adm)s")
print("\n".join(s) % params)
print """Generated card parameters :
> Name : %(name)s
> SMSP : %(smsp)s
> ICCID : %(iccid)s
> MCC/MNC : %(mcc)d/%(mnc)d
> IMSI : %(imsi)s
> Ki : %(ki)s
> OPC : %(opc)s
> ACC : %(acc)s
""" % params
def write_params_csv(opts, params):
@@ -473,27 +407,18 @@ def write_params_csv(opts, params):
cw.writerow([params[x] for x in row])
f.close()
def _read_params_csv(opts, iccid=None, imsi=None):
def _read_params_csv(opts, imsi):
import csv
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
f = open(opts.read_csv, 'r')
cr = csv.DictReader(f)
# Lower-case fieldnames
cr.fieldnames = [ field.lower() for field in cr.fieldnames ]
cr = csv.DictReader(f, row)
i = 0
if not 'iccid' in cr.fieldnames:
raise Exception("CSV file in wrong format!")
for row in cr:
if opts.num is not None and opts.read_iccid is False and opts.read_imsi is False:
if opts.num is not None and opts.read_imsi is False:
if opts.num == i:
f.close()
return row;
i += 1
if row['iccid'] == iccid:
f.close()
return row;
if row['imsi'] == imsi:
f.close()
return row;
@@ -501,36 +426,11 @@ def _read_params_csv(opts, iccid=None, imsi=None):
f.close()
return None
def read_params_csv(opts, imsi=None, iccid=None):
row = _read_params_csv(opts, iccid=iccid, imsi=imsi)
def read_params_csv(opts, imsi):
row = _read_params_csv(opts, imsi)
if row is not None:
row['mcc'] = row.get('mcc', mcc_from_imsi(row.get('imsi')))
row['mnc'] = row.get('mnc', mnc_from_imsi(row.get('imsi')))
pin_adm = None
# We need to escape the pin_adm we get from the csv
if 'pin_adm' in row:
pin_adm = ''.join(['%02x'%(ord(x)) for x in row['pin_adm']])
# Stay compatible to the odoo csv format
elif 'adm1' in row:
pin_adm = ''.join(['%02x'%(ord(x)) for x in row['adm1']])
if pin_adm:
row['pin_adm'] = rpad(pin_adm, 16)
# If the CSV-File defines a pin_adm_hex field use this field to
# generate pin_adm from that.
pin_adm_hex = row.get('pin_adm_hex')
if pin_adm_hex:
if len(pin_adm_hex) == 16:
row['pin_adm'] = pin_adm_hex
# Ensure that it's hex-encoded
try:
try_encode = h2b(pin_adm)
except ValueError:
raise ValueError("pin_adm_hex needs to be hex encoded using this option")
else:
raise ValueError("pin_adm_hex needs to be exactly 16 digits (hex encoded)")
row['mcc'] = int(row['mcc'])
row['mnc'] = int(row['mnc'])
return row
@@ -580,7 +480,7 @@ def init_batch(opts):
for k in BATCH_INCOMPATIBLE:
if getattr(opts, k):
print("Incompatible option with batch_state: %s" % (k,))
print "Incompatible option with batch_state: %s" % (k,)
sys.exit(-1)
# Don't load state if there is none ...
@@ -588,7 +488,7 @@ def init_batch(opts):
return
if not os.path.isfile(opts.batch_state):
print("No state file yet")
print "No state file yet"
return
# Get stored data
@@ -611,72 +511,34 @@ def save_batch(opts):
fh.close()
def process_card(opts, first, card_handler):
def card_detect(opts, scc):
if opts.dry_run is False:
# Connect transport
card_handler.get(first)
# Detect type if needed
card = None
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
if opts.type in ("auto", "auto_once"):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card:
print "Autodetected card type %s" % card.name
card.reset()
break
if opts.dry_run is False:
# Get card
card = card_detect(opts.type, scc)
if card is None:
print("No card detected!")
return -1
print "Autodetection failed"
return
# Probe only
if opts.probe:
return 0
if opts.type == "auto_once":
opts.type = card.name
# Erase if requested
if opts.erase:
print("Formatting ...")
card.erase()
card.reset()
elif opts.type in ctypes:
card = ctypes[opts.type](scc)
# Generate parameters
if opts.source == 'cmdline':
cp = gen_parameters(opts)
elif opts.source == 'csv':
imsi = None
iccid = None
if opts.read_iccid:
if opts.dry_run:
# Connect transport
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)
(res,_) = scc.read_binary(EF['IMSI'])
imsi = swap_nibbles(res)[3:]
else:
imsi = opts.imsi
cp = read_params_csv(opts, imsi=imsi, iccid=iccid)
if cp is None:
print("Error reading parameters from CSV file!\n")
return 2
print_parameters(cp)
if opts.dry_run is False:
# Program the card
print("Programming ...")
card.program(cp)
else:
print("Dry Run: NOT PROGRAMMING!")
raise ValueError("Unknown card type %s" % opts.type)
# Write parameters permanently
write_parameters(opts, cp)
# Batch mode state update and save
if opts.num is not None:
opts.num += 1
save_batch(opts)
card_handler.done()
return 0
return card
if __name__ == '__main__':
@@ -684,57 +546,88 @@ if __name__ == '__main__':
# Parse options
opts = parse_options()
# Init card reader driver
sl = init_reader(opts)
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
# If we use a CSV file as data input, check if the CSV file exists.
if opts.source == 'csv':
print("Using CSV file as data input: " + str(opts.read_csv))
if not os.path.isfile(opts.read_csv):
print("CSV file not found!")
sys.exit(1)
# Batch mode init
init_batch(opts)
if opts.card_handler:
card_handler = card_handler_auto(sl, opts.card_handler)
else:
card_handler = card_handler(sl)
# Iterate
done = False
first = True
card = None
while 1:
try:
rc = process_card(opts, first, card_handler)
except (KeyboardInterrupt):
print("")
print("Terminated by user!")
sys.exit(0)
except (SystemExit):
raise
except:
print("")
print("Card programming failed with an execption:")
print("---------------------8<---------------------")
traceback.print_exc()
print("---------------------8<---------------------")
print("")
rc = -1
while not done:
# Something did not work as well as expected, however, lets
# make sure the card is pulled from the reader.
if rc != 0:
card_handler.error()
# If we are not in batch mode we are done in any case, so lets
# exit here.
if not opts.batch_mode:
sys.exit(rc)
if opts.dry_run is False:
# Connect transport
print "Insert card now (or CTRL-C to cancel)"
sl.wait_for_card(newcardonly=not first)
# Not the first anymore !
first = False
if opts.dry_run is False:
# Get card
card = card_detect(opts, scc)
if card is None:
if opts.batch_mode:
first = False
continue
else:
sys.exit(-1)
# Erase if requested
if opts.erase:
print "Formatting ..."
card.erase()
card.reset()
# Generate parameters
if opts.source == 'cmdline':
cp = gen_parameters(opts)
elif opts.source == 'csv':
if opts.read_imsi:
if opts.dry_run:
# Connect transport
print "Insert card now (or CTRL-C to cancel)"
sl.wait_for_card(newcardonly=not first)
(res,_) = scc.read_binary(EF['IMSI'])
imsi = swap_nibbles(res)[3:]
else:
imsi = opts.imsi
cp = read_params_csv(opts, imsi)
if cp is None:
print "Error reading parameters\n"
sys.exit(2)
print_parameters(cp)
if opts.dry_run is False:
# Program the card
print "Programming ..."
if opts.dry_run is not True:
card.program(cp)
else:
print "Dry Run: NOT PROGRAMMING!"
# Write parameters permanently
write_parameters(opts, cp)
# Batch mode state update and save
if opts.num is not None:
opts.num += 1
save_batch(opts)
# Done for this card and maybe for everything ?
print "Done !\n"
if not opts.batch_mode:
done = True

126
pySim-read-all.py Executable file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python2
#
# Utility to display all files from a SIM card
#
#
# Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
# Copyright (C) 2013 Alexander Chemeris <alexander.chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import hashlib
from optparse import OptionParser
import os
import random
import re
import sys
try:
import json
except ImportError:
# Python < 2.5
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_select_ef_response
from pySim.ts_51_011 import EF, DF
def parse_options():
parser = OptionParser(usage="usage: %prog [options]")
parser.add_option("-d", "--device", dest="device", metavar="DEV",
help="Serial Device for SIM access [default: %default]",
default="/dev/ttyUSB0",
)
parser.add_option("-b", "--baud", dest="baudrate", type="int", metavar="BAUD",
help="Baudrate used for SIM access [default: %default]",
default=9600,
)
parser.add_option("-p", "--pcsc-device", dest="pcsc_dev", type='int', metavar="PCSC",
help="Which PC/SC reader number for SIM access",
default=None,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
# Wait for SIM card
sl.wait_for_card()
# Program the card
print("Reading ...")
# Read all
for (name, path) in EF.items():
try:
resp = scc.select_file(path)
(length, file_id, file_type, increase_cmd, access_cond,
file_status, data_len, ef_struct, record_len) = dec_select_ef_response(resp[-1])
# print name, resp
print name, (length, file_id, file_type, increase_cmd, access_cond, file_status, data_len, ef_struct, record_len)
if not access_cond[0] == '0' and not access_cond[0] == '1':
print("%s: Requires %s access to read." % (name, access_cond[0],))
continue
if ef_struct == '00':
# transparent
(res, sw) = scc.read_binary_selected(length)
if sw == '9000':
print("%s: %s" % (name, res,))
else:
print("%s: Can't read, response code = %s" % (name, sw,))
elif (ef_struct == '01' or ef_struct == '03') and record_len>0:
for i in range(1,length/record_len+1):
# linear fixed
(res, sw) = scc.read_record_selected(record_len, i)
if sw == '9000':
print("%s[%d]: %s" % (name, i, res,))
else:
print("%s[%d]: Can't read, response code = %s" % (name, i, sw,))
elif ef_struct == '03':
# cyclic
raise RuntimeError("Don't know how to read a cyclic EF")
else:
raise RuntimeError("Unknown EF type")
except RuntimeError as e:
print("%s: Can't read (%s)" % (name,e.message,))
# Done for this card and maybe for everything ?
print "Done !\n"

View File

@@ -28,14 +28,16 @@ import os
import random
import re
import sys
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
try:
import json
except ImportError:
# Python < 2.5
import simplejson as json
from pySim.commands import SimCardCommands
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_epdgid
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid
from pySim.ts_51_011 import EF, DF
def parse_options():
@@ -53,18 +55,6 @@ def parse_options():
help="Which PC/SC reader number for SIM access",
default=None,
)
parser.add_option("--modem-device", dest="modem_dev", metavar="DEV",
help="Serial port of modem for Generic SIM Access (3GPP TS 27.007)",
default=None,
)
parser.add_option("--modem-baud", dest="modem_baud", type="int", metavar="BAUD",
help="Baudrate used for modem's port [default: %default]",
default=115200,
)
parser.add_option("--osmocon", dest="osmocon_sock", metavar="PATH",
help="Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)",
default=None,
)
(options, args) = parser.parse_args()
@@ -79,8 +69,13 @@ if __name__ == '__main__':
# Parse options
opts = parse_options()
# Init card reader driver
sl = init_reader(opts)
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
@@ -88,121 +83,48 @@ if __name__ == '__main__':
# Wait for SIM card
sl.wait_for_card()
# Assuming UICC SIM
scc.cla_byte = "00"
scc.sel_ctrl = "0004"
# Testing for Classic SIM or UICC
(res, sw) = sl.send_apdu(scc.cla_byte + "a4" + scc.sel_ctrl + "02" + "3f00")
if sw == '6e00':
# Just a Classic SIM
scc.cla_byte = "a0"
scc.sel_ctrl = "0000"
# Program the card
print("Reading ...")
# Initialize Card object by auto detecting the card
card = card_detect("auto", scc) or Card(scc)
# Read all AIDs on the UICC
card.read_aids()
# EF.ICCID
(res, sw) = card.read_iccid()
(res, sw) = scc.read_binary(['3f00', '2fe2'])
if sw == '9000':
print("ICCID: %s" % (res,))
print("ICCID: %s" % (dec_iccid(res),))
else:
print("ICCID: Can't read, response code = %s" % (sw,))
# EF.IMSI
(res, sw) = card.read_imsi()
(res, sw) = scc.read_binary(['3f00', '7f20', '6f07'])
if sw == '9000':
print("IMSI: %s" % (res,))
print("IMSI: %s" % (dec_imsi(res),))
else:
print("IMSI: Can't read, response code = %s" % (sw,))
# EF.GID1
try:
(res, sw) = card.read_gid1()
if sw == '9000':
print("GID1: %s" % (res,))
else:
print("GID1: Can't read, response code = %s" % (sw,))
except Exception as e:
print("GID1: Can't read file -- %s" % (str(e),))
# EF.GID2
try:
(res, sw) = card.read_binary('GID2')
if sw == '9000':
print("GID2: %s" % (res,))
else:
print("GID2: Can't read, response code = %s" % (sw,))
except Exception as e:
print("GID2: Can't read file -- %s" % (str(e),))
# EF.SMSP
(res, sw) = card.read_record('SMSP', 1)
(res, sw) = scc.read_record(['3f00', '7f10', '6f42'], 1)
if sw == '9000':
print("SMSP: %s" % (res,))
else:
print("SMSP: Can't read, response code = %s" % (sw,))
# EF.SPN
try:
(res, sw) = card.read_spn()
if sw == '9000':
print("SPN: %s" % (res[0] or "Not available"))
print("Display HPLMN: %s" % (res[1],))
print("Display OPLMN: %s" % (res[2],))
else:
print("SPN: Can't read, response code = %s" % (sw,))
except Exception as e:
print("SPN: Can't read file -- %s" % (str(e),))
(res, sw) = scc.read_binary(EF['SPN'])
if sw == '9000':
print("SPN: %s" % (res,))
else:
print("SPN: Can't read, response code = %s" % (sw,))
# EF.PLMNsel
try:
(res, sw) = card.read_binary('PLMNsel')
if sw == '9000':
print("PLMNsel: %s" % (res))
else:
print("PLMNsel: Can't read, response code = %s" % (sw,))
except Exception as e:
print("PLMNsel: Can't read file -- " + str(e))
# EF.PLMNwAcT
try:
(res, sw) = card.read_plmn_act()
if sw == '9000':
print("PLMNwAcT:\n%s" % (res))
else:
print("PLMNwAcT: Can't read, response code = %s" % (sw,))
except Exception as e:
print("PLMNwAcT: Can't read file -- " + str(e))
# EF.OPLMNwAcT
try:
(res, sw) = card.read_oplmn_act()
if sw == '9000':
print("OPLMNwAcT:\n%s" % (res))
else:
print("OPLMNwAcT: Can't read, response code = %s" % (sw,))
except Exception as e:
print("OPLMNwAcT: Can't read file -- " + str(e))
# EF.HPLMNAcT
try:
(res, sw) = card.read_hplmn_act()
if sw == '9000':
print("HPLMNAcT:\n%s" % (res))
else:
print("HPLMNAcT: Can't read, response code = %s" % (sw,))
except Exception as e:
print("HPLMNAcT: Can't read file -- " + str(e))
# EF.HPLMN
(res, sw) = scc.read_binary(EF['PLMNsel'])
if sw == '9000':
print("HPLMN: %s" % (res))
# print("HPLMN: %s" % (dec_hplmn(res),))
else:
print("HPLMN: Can't read, response code = %s" % (sw,))
# FIXME
# EF.ACC
(res, sw) = card.read_binary('ACC')
(res, sw) = scc.read_binary(['3f00', '7f20', '6f78'])
if sw == '9000':
print("ACC: %s" % (res,))
else:
@@ -210,75 +132,17 @@ if __name__ == '__main__':
# EF.MSISDN
try:
(res, sw) = card.read_msisdn()
# print(scc.record_size(EF['MSISDN']))
(res, sw) = scc.read_record(EF['MSISDN'], 1)
if sw == '9000':
# (npi, ton, msisdn) = res
if res is not None:
print("MSISDN (NPI=%d ToN=%d): %s" % res)
if res[1] != 'f':
print("MSISDN: %s" % (res,))
else:
print("MSISDN: Not available")
print("MSISDN: %s (Not available)" % (res,))
else:
print("MSISDN: Can't read, response code = %s" % (sw,))
except Exception as e:
print("MSISDN: Can't read file -- " + str(e))
# EF.AD
(res, sw) = card.read_binary('AD')
if sw == '9000':
print("AD: %s" % (res,))
else:
print("AD: Can't read, response code = %s" % (sw,))
# EF.SST
(res, sw) = card.read_binary('SST')
if sw == '9000':
print("SIM Service Table: %s" % res)
# Print those which are available
print("%s" % dec_st(res))
else:
print("SIM Service Table: Can't read, response code = %s" % (sw,))
# 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():
# EF.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
(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:
(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))
# 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"):
# EF.IST
(res, sw) = card.read_binary('6f07')
if sw == '9000':
print("ISIM Service Table: %s" % res)
# Print those which are available
print("%s" % dec_st(res, table="isim"))
else:
print("ISIM Service Table: Can't read, response code = %s" % (sw,))
except:
print "MSISDN: Can't read. Probably not existing file"
# Done for this card and maybe for everything ?
print("Done !\n")
print "Done !\n"

97
pySim-run-gsm.py Executable file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python2
#
# Utility to run an A3/A8 algorithm on a SIM card
#
# Copyright (C) 2018 Alexander Chemeris <alexander.chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import sys
from optparse import OptionParser
from pySim.commands import SimCardCommands
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to run an A3/A8 algorithm on a SIM card. "
"Prints generated SRES and Kc for a given RAND number "
"and exits.")
parser.add_option("-d", "--device", dest="device", metavar="DEV",
help="Serial Device for SIM access [default: %default]",
default="/dev/ttyUSB0",
)
parser.add_option("-b", "--baud", dest="baudrate", type="int", metavar="BAUD",
help="Baudrate used for SIM access [default: %default]",
default=9600,
)
parser.add_option("-p", "--pcsc-device", dest="pcsc_dev", type='int', metavar="PCSC",
help="Which PC/SC reader number for SIM access",
default=None,
)
parser.add_option("-r", "--rand", dest="rand", metavar="RAND",
help="16 bytes of RAND value",
default=None,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
if opts.rand is None:
print("Please specify RAND value")
sys.exit(1)
if len(opts.rand) != 32:
print("RAND must be 16 bytes long")
sys.exit(1)
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
# Wait for SIM card
sl.wait_for_card()
# Program the card
print("Running GSM algorithm with RAND %s" % (opts.rand,))
# Run GSM A3/A8
(res, sw) = scc.run_gsm(opts.rand)
if sw == '9000':
sres, kc = res
print("SRES = %s" % (sres,))
print("Kc = %s" % (kc,))
else:
print("Error %s, result data '%s'" % (sw, res))
# Done for this card and maybe for everything ?
print "Done !\n"

View File

@@ -1,108 +0,0 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
""" pySim: card handler utilities
"""
#
# (C) 2019 by Sysmocom s.f.m.c. GmbH
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import subprocess
import sys
import yaml
# Manual card handler: User is prompted to insert/remove card from the reader.
class card_handler:
sl = None
def __init__(self, sl):
self.sl = sl
def get(self, first = False):
print("Ready for Programming: Insert card now (or CTRL-C to cancel)")
self.sl.wait_for_card(newcardonly=not first)
def error(self):
print("Programming failed: Remove card from reader")
print("")
def done(self):
print("Programming successful: Remove card from reader")
print("")
# Automatic card handler: A machine is used to handle the cards.
class card_handler_auto:
sl = None
cmds = None
verbose = True
def __init__(self, sl, config_file):
print("Card handler Config-file: " + str(config_file))
self.sl = sl
with open(config_file) as cfg:
self.cmds = yaml.load(cfg, Loader=yaml.FullLoader)
self.verbose = (self.cmds.get('verbose') == True)
def __print_outout(self,out):
print("")
print("Card handler output:")
print("---------------------8<---------------------")
stdout = out[0].strip()
if len(stdout) > 0:
print("stdout:")
print(stdout)
stderr = out[1].strip()
if len(stderr) > 0:
print("stderr:")
print(stderr)
print("---------------------8<---------------------")
print("")
def __exec_cmd(self, command):
print("Card handler Commandline: " + str(command))
proc = subprocess.Popen([command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out = proc.communicate()
rc = proc.returncode
if rc != 0 or self.verbose:
self.__print_outout(out)
if rc != 0:
print("")
print("Error: Card handler failure! (rc=" + str(rc) + ")")
sys.exit(rc)
def get(self, first = False):
print("Ready for Programming: Transporting card into the reader-bay...")
self.__exec_cmd(self.cmds['get'])
self.sl.connect()
def error(self):
print("Programming failed: Transporting card to the error-bin...")
self.__exec_cmd(self.cmds['error'])
print("")
def done(self):
print("Programming successful: Transporting card into the collector bin...")
self.__exec_cmd(self.cmds['done'])
print("")

View File

@@ -24,7 +24,6 @@
#
from pySim.ts_51_011 import EF, DF
from pySim.ts_31_102 import EF_USIM_ADF_map
from pySim.utils import *
from smartcard.util import toBytes
@@ -33,22 +32,10 @@ class Card(object):
def __init__(self, scc):
self._scc = scc
self._adm_chv_num = 4
self._aids = []
def reset(self):
self._scc.reset_card()
def erase(self):
print("warning: erasing is not supported for specified card type!")
return
def file_exists(self, fid):
res_arr = self._scc.try_select_file(fid)
for res in res_arr:
if res[1] != '9000':
return False
return True
def verify_adm(self, key):
'''
Authenticate with ADM key
@@ -78,13 +65,6 @@ class Card(object):
data, sw = self._scc.update_binary(EF['ACC'], lpad(acc, 4))
return sw
def read_hplmn_act(self):
(res, sw) = self._scc.read_binary(EF['HPLMNAcT'])
if sw == '9000':
return (format_xplmn_w_act(res), sw)
else:
return (None, sw)
def update_hplmn_act(self, mcc, mnc, access_tech='FFFF'):
"""
Update Home PLMN with access technology bit-field
@@ -96,80 +76,17 @@ class Card(object):
access_tech = 'FFFF' # All technologues selected, even Reserved for Future Use ones
"""
# get size and write EF.HPLMNwAcT
data = self._scc.read_binary(EF['HPLMNwAcT'], length=None, offset=0)
size = len(data[0]) // 2
r = self._scc.select_file(EF['HPLMNwAcT'])
size = int(r[-1][4:8], 16)
hplmn = enc_plmn(mcc, mnc)
content = hplmn + access_tech
data, sw = self._scc.update_binary(EF['HPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
return sw
def read_oplmn_act(self):
(res, sw) = self._scc.read_binary(EF['OPLMNwAcT'])
if sw == '9000':
return (format_xplmn_w_act(res), sw)
else:
return (None, sw)
def update_oplmn_act(self, mcc, mnc, access_tech='FFFF'):
"""
See note in update_hplmn_act()
"""
# get size and write EF.OPLMNwAcT
data = self._scc.read_binary(EF['OPLMNwAcT'], length=None, offset=0)
size = len(data[0]) // 2
hplmn = enc_plmn(mcc, mnc)
content = hplmn + access_tech
data, sw = self._scc.update_binary(EF['OPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
return sw
def read_plmn_act(self):
(res, sw) = self._scc.read_binary(EF['PLMNwAcT'])
if sw == '9000':
return (format_xplmn_w_act(res), sw)
else:
return (None, sw)
def update_plmn_act(self, mcc, mnc, access_tech='FFFF'):
"""
See note in update_hplmn_act()
"""
# get size and write EF.PLMNwAcT
data = self._scc.read_binary(EF['PLMNwAcT'], length=None, offset=0)
size = len(data[0]) // 2
hplmn = enc_plmn(mcc, mnc)
content = hplmn + access_tech
data, sw = self._scc.update_binary(EF['PLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
return sw
def update_plmnsel(self, mcc, mnc):
data = self._scc.read_binary(EF['PLMNsel'], length=None, offset=0)
size = len(data[0]) // 2
hplmn = enc_plmn(mcc, mnc)
data, sw = self._scc.update_binary(EF['PLMNsel'], hplmn + 'ff' * (size-3))
data, sw = self._scc.update_binary(EF['HPLMNwAcT'], content + 'ffffff0000' * (size/5-1))
return sw
def update_smsp(self, smsp):
data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 84))
return sw
def update_ad(self, mnc):
#See also: 3GPP TS 31.102, chapter 4.2.18
mnclen = len(str(mnc))
if mnclen == 1:
mnclen = 2
if mnclen > 3:
raise RuntimeError('unable to calculate proper mnclen')
data, sw = self._scc.read_binary(EF['AD'], length=None, offset=0)
# Reset contents to EF.AD in case the file is uninintalized
if data.lower() == "ffffffff":
data = "00000000"
content = data[0:6] + "%02X" % mnclen
data, sw = self._scc.update_binary(EF['AD'], content)
return sw
def read_spn(self):
(spn, sw) = self._scc.read_binary(EF['SPN'])
if sw == '9000':
@@ -182,113 +99,6 @@ class Card(object):
data, sw = self._scc.update_binary(EF['SPN'], rpad(content, 32))
return sw
def read_binary(self, ef, length=None, offset=0):
ef_path = ef in EF and EF[ef] or ef
return self._scc.read_binary(ef_path, length, offset)
def read_record(self, ef, rec_no):
ef_path = ef in EF and EF[ef] or ef
return self._scc.read_record(ef_path, rec_no)
def read_gid1(self):
(res, sw) = self._scc.read_binary(EF['GID1'])
if sw == '9000':
return (res, sw)
else:
return (None, sw)
def read_msisdn(self):
(res, sw) = self._scc.read_record(EF['MSISDN'], 1)
if sw == '9000':
return (dec_msisdn(res), sw)
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:
# Find out how many records the EF.DIR has
# and store all the AIDs in the UICC
rec_cnt = self._scc.record_count(EF['DIR'])
for i in range(0, rec_cnt):
rec = self._scc.read_record(EF['DIR'], i + 1)
if (rec[0][0:2], rec[0][4:6]) == ('61', '4f') and len(rec[0]) > 12 \
and rec[0][8:8 + int(rec[0][6:8], 16) * 2] not in self._aids:
self._aids.append(rec[0][8:8 + int(rec[0][6:8], 16) * 2])
except Exception as e:
print("Can't read AIDs from SIM -- %s" % (str(e),))
# Select ADF.U/ISIM in the Card using its full AID
def select_adf_by_aid(self, adf="usim"):
# Check for valid ADF name
if adf not in ["usim", "isim"]:
return None
# First (known) halves of the U/ISIM AID
aid_map = {}
aid_map["usim"] = "a0000000871002"
aid_map["isim"] = "a0000000871004"
for aid in self._aids:
if aid_map[adf] in aid:
(res, sw) = self._scc.select_adf(aid)
return sw
return None
# Erase the contents of a file
def erase_binary(self, ef):
len = self._scc.binary_size(ef)
self._scc.update_binary(ef, "ff" * len, offset=0, verify=True)
# Erase the contents of a single record
def erase_record(self, ef, rec_no):
len = self._scc.record_size(ef)
self._scc.update_record(ef, rec_no, "ff" * len, force_len=False, verify=True)
class UsimCard(Card):
def __init__(self, ssc):
super(UsimCard, self).__init__(ssc)
def read_ehplmn(self):
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'])
if sw == '9000':
return (format_xplmn(res), sw)
else:
return (None, sw)
def update_ehplmn(self, mcc, mnc):
data = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
size = len(data[0]) // 2
ehplmn = enc_plmn(mcc, mnc)
data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
return sw
class _MagicSimBase(Card):
"""
@@ -296,7 +106,7 @@ class _MagicSimBase(Card):
each possible provider uses a specific record number in each EF. The
indexes used are ( where N is the number of providers supported ) :
- [2 .. N+1] for the operator name
- [1 .. N] for the programable EFs
- [1 .. N] for the programable EFs
* 3f00/7f4d/8f0c : Operator Name
@@ -483,12 +293,12 @@ class FakeMagicSim(Card):
# Set first entry
entry = (
'81' + # 1b Status: Valid & Active
'81' + # 1b Status: Valid & Active
rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
enc_iccid(p['iccid']) + # 10b ICCID
enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
p['ki'] + # 16b Ki
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
enc_iccid(p['iccid']) + # 10b ICCID
enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
p['ki'] + # 16b Ki
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
)
self._scc.update_record('000c', 1, entry)
@@ -521,7 +331,7 @@ class GrcardSim(Card):
# Authenticate using ADM PIN 5
if p['pin_adm']:
pin = h2b(p['pin_adm'])
pin = p['pin_adm']
else:
pin = h2b("4444444444444444")
self._scc.verify_chv(5, pin)
@@ -539,9 +349,8 @@ class GrcardSim(Card):
data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
# EF.SMSP
if p.get('smsp'):
r = self._scc.select_file(['3f00', '7f10', '6f42'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
r = self._scc.select_file(['3f00', '7f10', '6f42'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
# Set the Ki using proprietary command
pdu = '80d4020010' + p['ki']
@@ -560,6 +369,8 @@ class GrcardSim(Card):
# FIXME: EF.MSISDN
def erase(self):
return
class SysmoSIMgr1(GrcardSim):
"""
@@ -569,17 +380,8 @@ class SysmoSIMgr1(GrcardSim):
"""
name = 'sysmosim-gr1'
@classmethod
def autodetect(kls, scc):
try:
# Look for ATR
if scc.get_atr() == toBytes("3B 99 18 00 11 88 22 33 44 55 66 77 60"):
return kls(scc)
except:
return None
return None
class SysmoUSIMgr1(UsimCard):
class SysmoUSIMgr1(Card):
"""
sysmocom sysmoUSIM-GR1
"""
@@ -604,6 +406,9 @@ class SysmoUSIMgr1(UsimCard):
)
data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
def erase(self):
return
class SysmoSIMgr2(Card):
"""
@@ -636,7 +441,7 @@ class SysmoSIMgr2(Card):
# P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK
# P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10)
if p['pin_adm']:
pin = h2b(p['pin_adm'])
pin = p['pin_adm']
else:
pin = h2b("4444444444444444")
@@ -676,11 +481,12 @@ class SysmoSIMgr2(Card):
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))
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
def erase(self):
return
class SysmoUSIMSJS1(UsimCard):
class SysmoUSIMSJS1(Card):
"""
sysmocom sysmoUSIM-SJS1
"""
@@ -690,7 +496,7 @@ class SysmoUSIMSJS1(UsimCard):
def __init__(self, ssc):
super(SysmoUSIMSJS1, self).__init__(ssc)
self._scc.cla_byte = "00"
self._scc.sel_ctrl = "0004" #request an FCP
self._scc.sel_ctrl = "000C"
@classmethod
def autodetect(kls, scc):
@@ -721,67 +527,20 @@ class SysmoUSIMSJS1(UsimCard):
# set Ki in proprietary file
data, sw = self._scc.update_binary('00FF', p['ki'])
# set OPc in proprietary file
if 'opc' in p:
content = "01" + p['opc']
data, sw = self._scc.update_binary('00F7', content)
# set Ki in proprietary file
content = "01" + p['opc']
data, sw = self._scc.update_binary('00F7', content)
# set Service Provider Name
if p.get('name') is not None:
content = enc_spn(p['name'], True, True)
data, sw = self._scc.update_binary('6F46', rpad(content, 32))
if p.get('acc') is not None:
self.update_acc(p['acc'])
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
# EF.PLMNsel
if p.get('mcc') and p.get('mnc'):
sw = self.update_plmnsel(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming PLMNsel failed with code %s"%sw)
# EF.PLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_plmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming PLMNwAcT failed with code %s"%sw)
# EF.OPLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_oplmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming OPLMNwAcT failed with code %s"%sw)
# EF.HPLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_hplmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming HPLMNwAcT failed with code %s"%sw)
# EF.AD
if p.get('mcc') and p.get('mnc'):
sw = self.update_ad(p['mnc'])
if sw != '9000':
print("Programming AD failed with code %s"%sw)
# EF.SMSP
if p.get('smsp'):
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
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'])
data = 'ff' * 20 + msisdn + 'ff' * 2
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
def erase(self):
return
class FairwavesSIM(Card):
@@ -791,20 +550,30 @@ class FairwavesSIM(Card):
The SIM card is operating according to the standard.
For Ki/OP/OPC programming the following files are additionally open for writing:
3F00/7F20/FF01 OP/OPC:
byte 1 = 0x01, bytes 2-17: OPC;
byte 1 = 0x00, bytes 2-17: OP;
byte 1 = 0x01, bytes 2-17: OPC;
byte 1 = 0x00, bytes 2-17: OP;
3F00/7F20/FF02: Ki
3F00/7F20/FF03: 2G/3G auth algorithm
byte 1 = GSM SIM A3/A8 algorithm selection
byte 2 = USIM A3/A8 algorithm selection
Algorithms:
0x01 = Milenage
0x03 = COMP128v1
0x06 = COMP128v2
0x07 = COMP128v3
"""
name = 'Fairwaves-SIM'
name = 'Fairwaves SIM'
# Propriatary files
_EF_num = {
'Ki': 'FF02',
'OP/OPC': 'FF01',
'A3A8': 'FF03',
}
_EF = {
'Ki': DF['GSM']+[_EF_num['Ki']],
'OP/OPC': DF['GSM']+[_EF_num['OP/OPC']],
'A3A8': DF['GSM']+[_EF_num['A3A8']],
}
def __init__(self, ssc):
@@ -888,6 +657,14 @@ class FairwavesSIM(Card):
data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
return sw
def read_a3a8(self):
(ef, sw) = self._scc.read_binary(self._EF['A3A8'])
return (ef, sw)
def update_a3a8(self, content):
(ef, sw) = self._scc.update_binary(self._EF['A3A8'], content)
return (ef, sw)
def program(self, p):
# authenticate as ADM1
@@ -924,276 +701,14 @@ class FairwavesSIM(Card):
if sw != '9000':
print("Programming ACC failed with code %s"%sw)
class OpenCellsSim(Card):
"""
OpenCellsSim
"""
name = 'OpenCells-SIM'
def __init__(self, ssc):
super(OpenCellsSim, self).__init__(ssc)
self._adm_chv_num = 0x0A
@classmethod
def autodetect(kls, scc):
try:
# Look for ATR
if scc.get_atr() == toBytes("3B 9F 95 80 1F C3 80 31 E0 73 FE 21 13 57 86 81 02 86 98 44 18 A8"):
return kls(scc)
except:
return None
return None
def program(self, p):
if not p['pin_adm']:
raise ValueError("Please provide a PIN-ADM as there is no default one")
self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
# select MF
r = self._scc.select_file(['3f00'])
# write EF.ICCID
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
r = self._scc.select_file(['7ff0'])
# set Ki in proprietary file
data, sw = self._scc.update_binary('FF02', p['ki'])
# set OPC in proprietary file
data, sw = self._scc.update_binary('FF01', p['opc'])
# select DF_GSM
r = self._scc.select_file(['7f20'])
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
class WavemobileSim(Card):
"""
WavemobileSim
"""
name = 'Wavemobile-SIM'
def __init__(self, ssc):
super(WavemobileSim, self).__init__(ssc)
self._adm_chv_num = 0x0A
self._scc.cla_byte = "00"
self._scc.sel_ctrl = "0004" #request an FCP
@classmethod
def autodetect(kls, scc):
try:
# Look for ATR
if scc.get_atr() == toBytes("3B 9F 95 80 1F C7 80 31 E0 73 F6 21 13 67 4D 45 16 00 43 01 00 8F"):
return kls(scc)
except:
return None
return None
def program(self, p):
if not p['pin_adm']:
raise ValueError("Please provide a PIN-ADM as there is no default one")
sw = self.verify_adm(h2b(p['pin_adm']))
if sw != '9000':
raise RuntimeError('Failed to authenticate with ADM key %s'%(p['pin_adm'],))
# EF.ICCID
# TODO: Add programming of the ICCID
if p.get('iccid'):
print("Warning: Programming of the ICCID is not implemented for this type of card.")
# KI (Presumably a propritary file)
# TODO: Add programming of KI
if p.get('ki'):
print("Warning: Programming of the KI is not implemented for this type of card.")
# OPc (Presumably a propritary file)
# TODO: Add programming of OPc
if p.get('opc'):
print("Warning: Programming of the OPc is not implemented for this type of card.")
# EF.SMSP
if p.get('smsp'):
sw = self.update_smsp(p['smsp'])
if sw != '9000':
print("Programming SMSP failed with code %s"%sw)
# EF.IMSI
if p.get('imsi'):
sw = self.update_imsi(p['imsi'])
if sw != '9000':
print("Programming IMSI failed with code %s"%sw)
# EF.ACC
if p.get('acc'):
sw = self.update_acc(p['acc'])
if sw != '9000':
print("Programming ACC failed with code %s"%sw)
# EF.PLMNsel
if p.get('mcc') and p.get('mnc'):
sw = self.update_plmnsel(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming PLMNsel failed with code %s"%sw)
# EF.PLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_plmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming PLMNwAcT failed with code %s"%sw)
# EF.OPLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_oplmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming OPLMNwAcT failed with code %s"%sw)
# EF.AD
if p.get('mcc') and p.get('mnc'):
sw = self.update_ad(p['mnc'])
if sw != '9000':
print("Programming AD failed with code %s"%sw)
return None
class SysmoISIMSJA2(UsimCard):
"""
sysmocom sysmoISIM-SJA2
"""
name = 'sysmoISIM-SJA2'
def __init__(self, ssc):
super(SysmoISIMSJA2, self).__init__(ssc)
self._scc.cla_byte = "00"
self._scc.sel_ctrl = "0004" #request an FCP
@classmethod
def autodetect(kls, scc):
try:
# Try card model #1
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9"
if scc.get_atr() == toBytes(atr):
return kls(scc)
# Try card model #2
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2"
if scc.get_atr() == toBytes(atr):
return kls(scc)
# Try card model #3
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"
if scc.get_atr() == toBytes(atr):
return kls(scc)
except:
return None
return None
def program(self, p):
# authenticate as ADM using default key (written on the card..)
if not p['pin_adm']:
raise ValueError("Please provide a PIN-ADM as there is no default one")
self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
# This type of card does not allow to reprogram the ICCID.
# Reprogramming the ICCID would mess up the card os software
# license management, so the ICCID must be kept at its factory
# setting!
if p.get('iccid'):
print("Warning: Programming of the ICCID is not implemented for this type of card.")
# select DF_GSM
self._scc.select_file(['7f20'])
# write EF.IMSI
if p.get('imsi'):
self._scc.update_binary('6f07', enc_imsi(p['imsi']))
# EF.PLMNsel
if p.get('mcc') and p.get('mnc'):
sw = self.update_plmnsel(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming PLMNsel failed with code %s"%sw)
# EF.PLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_plmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming PLMNwAcT failed with code %s"%sw)
# EF.OPLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_oplmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming OPLMNwAcT failed with code %s"%sw)
# EF.HPLMNwAcT
if p.get('mcc') and p.get('mnc'):
sw = self.update_hplmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming HPLMNwAcT failed with code %s"%sw)
# EF.AD
if p.get('mcc') and p.get('mnc'):
sw = self.update_ad(p['mnc'])
if sw != '9000':
print("Programming AD failed with code %s"%sw)
# EF.SMSP
if p.get('smsp'):
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
# 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'])
self._scc.select_file(['a515'])
if p.get('ki'):
self._scc.update_binary('6f20', p['ki'], 1)
if p.get('opc'):
self._scc.update_binary('6f20', p['opc'], 17)
# update EF-USIM_AUTH_KEY in 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)
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'):
self._scc.update_binary('af20', p['opc'], 17)
# update EF.EHPLMN in ADF.USIM
if self.file_exists(EF_USIM_ADF_map['EHPLMN']):
if p.get('mcc') and p.get('mnc'):
sw = self.update_ehplmn(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming EHPLMN failed with code %s"%sw)
def erase(self):
return
# In order for autodetection ...
# In order for autodetection ...
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2 ]
FairwavesSIM ]
def card_autodetect(scc):
for kls in _cards_classes:
@@ -1202,31 +717,3 @@ def card_autodetect(scc):
card.reset()
return card
return None
def card_detect(ctype, scc):
# Detect type if needed
card = None
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
if ctype in ("auto", "auto_once"):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card:
print("Autodetected card type: %s" % card.name)
card.reset()
break
if card is None:
print("Autodetection failed")
return None
if ctype == "auto_once":
ctype = card.name
elif ctype in ctypes:
card = ctypes[ctype](scc)
else:
raise ValueError("Unknown card type: %s" % ctype)
return card

View File

@@ -24,65 +24,13 @@
from pySim.utils import rpad, b2h
class SimCardCommands(object):
def __init__(self, transport):
self._tp = transport;
self._cla_byte = "a0"
self.sel_ctrl = "0000"
# Extract a single FCP item from TLV
def __parse_fcp(self, fcp):
# see also: ETSI TS 102 221, chapter 11.1.1.3.1 Response for MF,
# DF or ADF
from pytlv.TLV import TLV
tlvparser = TLV(['82', '83', '84', 'a5', '8a', '8b', '8c', '80', 'ab', 'c6', '81', '88'])
# pytlv is case sensitive!
fcp = fcp.lower()
if fcp[0:2] != '62':
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
# coded as one or two byte vale, so we have to try it out by
# checking if the length of the remaining TLV string matches
# what we get in the length field.
# See also ETSI TS 102 221, chapter 11.1.1.3.0 Base coding.
exp_tlv_len = int(fcp[2:4], 16)
if len(fcp[4:]) // 2 == exp_tlv_len:
skip = 4
else:
exp_tlv_len = int(fcp[2:6], 16)
if len(fcp[4:]) // 2 == exp_tlv_len:
skip = 6
# Skip FCP tag and length
tlv = fcp[skip:]
return tlvparser.parse(tlv)
# Tell the length of a record by the card response
# USIMs respond with an FCP template, which is different
# from what SIMs responds. See also:
# USIM: ETSI TS 102 221, chapter 11.1.1.3 Response Data
# SIM: GSM 11.11, chapter 9.2.1 SELECT
def __record_len(self, r):
if self.sel_ctrl == "0004":
tlv_parsed = self.__parse_fcp(r[-1])
file_descriptor = tlv_parsed['82']
# See also ETSI TS 102 221, chapter 11.1.1.4.3 File Descriptor
return int(file_descriptor[4:8], 16)
else:
return int(r[-1][28:30], 16)
# Tell the length of a binary file. See also comment
# above.
def __len(self, r):
if self.sel_ctrl == "0004":
tlv_parsed = self.__parse_fcp(r[-1])
return int(tlv_parsed['80'], 16)
else:
return int(r[-1][4:8], 16)
def get_atr(self):
return self._tp.get_atr()
@@ -100,104 +48,84 @@ class SimCardCommands(object):
def sel_ctrl(self, value):
self._sel_ctrl = value
def try_select_file(self, dir_list):
rv = []
if type(dir_list) is not list:
dir_list = [dir_list]
for i in dir_list:
data, sw = self._tp.send_apdu(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
rv.append((data, sw))
if sw != '9000':
return rv
return rv
def select_file(self, dir_list):
rv = []
if type(dir_list) is not list:
dir_list = [dir_list]
for i in dir_list:
data, sw = self._tp.send_apdu_checksw(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
rv.append(data)
return rv
def select_adf(self, aid):
aidlen = ("0" + format(len(aid) // 2, 'x'))[-2:]
return self._tp.send_apdu_checksw(self.cla_byte + "a4" + "0404" + aidlen + aid)
def read_binary_selected(self, length, offset=0):
pdu = self.cla_byte + 'b0%04x%02x' % (offset, (min(256, length) & 0xff))
return self._tp.send_apdu(pdu)
def read_binary(self, ef, length=None, offset=0):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
if len(r[-1]) == 0:
return (None, None)
if length is None:
length = self.__len(r) - offset
total_data = ''
while offset < length:
chunk_len = min(255, length-offset)
pdu = self.cla_byte + 'b0%04x%02x' % (offset, chunk_len)
data,sw = self._tp.send_apdu(pdu)
if sw == '9000':
total_data += data
offset += chunk_len
else:
raise ValueError('Failed to read (offset %d)' % (offset))
return total_data, sw
length = int(r[-1][4:8], 16) - offset
return self.read_binary_selected(length, offset)
def update_binary(self, ef, data, offset=0, verify=False):
def update_binary(self, ef, data, offset=0):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
self.select_file(ef)
pdu = self.cla_byte + 'd6%04x%02x' % (offset, len(data) // 2) + data
res = self._tp.send_apdu_checksw(pdu)
if verify:
self.verify_binary(ef, data, offset)
return res
pdu = self.cla_byte + 'd6%04x%02x' % (offset, len(data)/2) + data
return self._tp.send_apdu_checksw(pdu)
def verify_binary(self, ef, data, offset=0):
res = self.read_binary(ef, len(data) // 2, offset)
if res[0].lower() != data.lower():
raise ValueError('Binary verification failed (expected %s, got %s)' % (data.lower(), res[0].lower()))
def read_record(self, ef, rec_no):
r = self.select_file(ef)
rec_length = self.__record_len(r)
def read_record_selected(self, rec_length, rec_no):
pdu = self.cla_byte + 'b2%02x04%02x' % (rec_no, rec_length)
return self._tp.send_apdu(pdu)
def update_record(self, ef, rec_no, data, force_len=False, verify=False):
def read_record(self, ef, rec_no):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
rec_length = int(r[-1][28:30], 16)
return self.read_record_selected(rec_length, rec_no)
def update_record(self, ef, rec_no, data, force_len=False):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
if not force_len:
rec_length = self.__record_len(r)
if (len(data) // 2 != rec_length):
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data) // 2))
rec_length = int(r[-1][28:30], 16)
if (len(data)/2 != rec_length):
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data)/2))
else:
rec_length = len(data) // 2
rec_length = len(data)/2
pdu = (self.cla_byte + 'dc%02x04%02x' % (rec_no, rec_length)) + data
res = self._tp.send_apdu_checksw(pdu)
if verify:
self.verify_record(ef, rec_no, data)
return res
def verify_record(self, ef, rec_no, data):
res = self.read_record(ef, rec_no)
if res[0].lower() != data.lower():
raise ValueError('Record verification failed (expected %s, got %s)' % (data.lower(), res[0].lower()))
return self._tp.send_apdu_checksw(pdu)
def record_size(self, ef):
r = self.select_file(ef)
return self.__record_len(r)
return int(r[-1][28:30], 16)
def record_count(self, ef):
r = self.select_file(ef)
return self.__len(r) // self.__record_len(r)
return int(r[-1][4:8], 16) // int(r[-1][28:30], 16)
def binary_size(self, ef):
r = self.select_file(ef)
return self.__len(r)
def run_gsm(self, rand):
def run_gsm_raw(self, rand):
'''
A3/A8 algorithm in the SIM card using the given RAND.
This function returns a raw result tuple.
'''
if len(rand) != 32:
raise ValueError('Invalid rand')
self.select_file(['3f00', '7f20'])
return self._tp.send_apdu(self.cla_byte + '88000010' + rand)
def run_gsm(self, rand):
'''
A3/A8 algorithm in the SIM card using the given RAND.
This function returns a parsed ((SRES, Kc), sw) tuple.
'''
(res, sw) = self.run_gsm_raw(rand)
if sw != '9000':
return (res, sw)
return ((res[0:8], res[8:]), sw)
def reset_card(self):
return self._tp.reset_card()

View File

@@ -23,17 +23,11 @@
from __future__ import absolute_import
try:
# This is for compatibility with python 2 and 3
from exceptions import Exception
except:
import exceptions
class NoCardError(exceptions.Exception):
pass
class NoCardError(Exception):
pass
class ProtocolError(Exception):
pass
class ReaderError(Exception):
class ProtocolError(exceptions.Exception):
pass

View File

@@ -28,7 +28,7 @@ class LinkBase(object):
timeout : Maximum wait time (None=no timeout)
newcardonly : Should we wait for a new card, or an already
inserted one ?
inserted one ?
"""
pass
@@ -52,8 +52,8 @@ class LinkBase(object):
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
return : tuple(data, sw), where
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
"""
pass
@@ -62,18 +62,12 @@ class LinkBase(object):
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
return : tuple(data, sw), where
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
"""
data, sw = self.send_apdu_raw(pdu)
# When whe 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
# xx is the number of response bytes available.
# See also:
# SW1=9F: 3GPP TS 51.011 9.4.1, Responses to commands which are correctly executed
# SW1=61: ISO/IEC 7816-4, Table 5 — General meaning of the interindustry values of SW1-SW2
if (sw is not None) and ((sw[0:2] == '9f') or (sw[0:2] == '61')):
if (sw is not None) and (sw[0:2] == '9f'):
pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
data, sw = self.send_apdu_raw(pdu_gr)
@@ -83,23 +77,12 @@ class LinkBase(object):
"""send_apdu_checksw(pdu,sw): Sends an APDU and check returned SW
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
sw : string of 4 hexadecimal characters (ex. "9000"). The
user may mask out certain digits using a '?' to add some
ambiguity if needed.
sw : string of 4 hexadecimal characters (ex. "9000")
return : tuple(data, sw), where
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
"""
rv = self.send_apdu(pdu)
# Create a masked version of the returned status word
sw_masked = ""
for i in range(0, 4):
if sw.lower()[i] == '?':
sw_masked = sw_masked + '?'
else:
sw_masked = sw_masked + rv[1][i].lower()
if sw.lower() != sw_masked:
raise RuntimeError("SW match failed! Expected %s and got %s." % (sw.lower(), rv[1]))
if sw.lower() != rv[1]:
raise RuntimeError("SW match failed ! Expected %s and got %s." % (sw.lower(), rv[1]))
return rv

View File

@@ -1,157 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" pySim: Transport Link for Calypso bases phones
"""
#
# Copyright (C) 2018 Vadim Yanitskiy <axilirator@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import
import select
import struct
import socket
import os
from pySim.transport import LinkBase
from pySim.exceptions import *
from pySim.utils import h2b, b2h
class L1CTLMessage(object):
# Every (encoded) L1CTL message has the following structure:
# - msg_length (2 bytes, net order)
# - l1ctl_hdr (packed structure)
# - msg_type
# - flags
# - padding (2 spare bytes)
# - ... payload ...
def __init__(self, msg_type, flags = 0x00):
# Init L1CTL message header
self.data = struct.pack("BBxx", msg_type, flags)
def gen_msg(self):
return struct.pack("!H", len(self.data)) + self.data
class L1CTLMessageReset(L1CTLMessage):
# L1CTL message types
L1CTL_RESET_REQ = 0x0d
L1CTL_RESET_IND = 0x07
L1CTL_RESET_CONF = 0x0e
# Reset types
L1CTL_RES_T_BOOT = 0x00
L1CTL_RES_T_FULL = 0x01
L1CTL_RES_T_SCHED = 0x02
def __init__(self, type = L1CTL_RES_T_FULL):
super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
self.data += struct.pack("Bxxx", type)
class L1CTLMessageSIM(L1CTLMessage):
# SIM related message types
L1CTL_SIM_REQ = 0x16
L1CTL_SIM_CONF = 0x17
def __init__(self, pdu):
super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
self.data += pdu
class CalypsoSimLink(LinkBase):
def __init__(self, sock_path = "/tmp/osmocom_l2"):
# Make sure that a given socket path exists
if not os.path.exists(sock_path):
raise ReaderError("There is no such ('%s') UNIX socket" % sock_path)
print("Connecting to osmocon at '%s'..." % sock_path)
# Establish a client connection
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect(sock_path)
def __del__(self):
self.sock.close()
def wait_for_rsp(self, exp_len = 128):
# Wait for incoming data (timeout is 3 seconds)
s, _, _ = select.select([self.sock], [], [], 3.0)
if not s:
raise ReaderError("Timeout waiting for card response")
# Receive expected amount of bytes from osmocon
rsp = self.sock.recv(exp_len)
return rsp
def reset_card(self):
# Request FULL reset
req_msg = L1CTLMessageReset()
self.sock.send(req_msg.gen_msg())
# Wait for confirmation
rsp = self.wait_for_rsp()
rsp_msg = struct.unpack_from("!HB", rsp)
if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
raise ReaderError("Failed to reset Calypso PHY")
def connect(self):
self.reset_card()
def disconnect(self):
pass # Nothing to do really ...
def wait_for_card(self, timeout = None, newcardonly = False):
pass # Nothing to do really ...
def send_apdu_raw(self, pdu):
"""see LinkBase.send_apdu_raw"""
# Request FULL reset
req_msg = L1CTLMessageSIM(h2b(pdu))
self.sock.send(req_msg.gen_msg())
# Read message length first
rsp = self.wait_for_rsp(struct.calcsize("!H"))
msg_len = struct.unpack_from("!H", rsp)[0]
if msg_len < struct.calcsize("BBxx"):
raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
# Read the whole message then
rsp = self.sock.recv(msg_len)
# Verify L1CTL header
hdr = struct.unpack_from("BBxx", rsp)
if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
raise ReaderError("Unexpected L1CTL message received")
# Verify the payload length
offset = struct.calcsize("BBxx")
if len(rsp) <= offset:
raise ProtocolError("Empty response from SIM?!?")
# Omit L1CTL header
rsp = rsp[offset:]
# Unpack data and SW
data = rsp[:-2]
sw = rsp[-2:]
return b2h(data), b2h(sw)

View File

@@ -1,126 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" pySim: Transport Link for 3GPP TS 27.007 compliant modems
"""
# Copyright (C) 2020 Vadim Yanitskiy <axilirator@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import
import logging as log
import serial
import time
import re
from pySim.transport import LinkBase
from pySim.exceptions import *
# HACK: if somebody needs to debug this thing
# log.root.setLevel(log.DEBUG)
class ModemATCommandLink(LinkBase):
def __init__(self, device='/dev/ttyUSB0', baudrate=115200):
self._sl = serial.Serial(device, baudrate, timeout=5)
self._device = device
self._atr = None
# Trigger initial reset
self.reset_card()
def __del__(self):
self._sl.close()
def send_at_cmd(self, cmd):
# Convert from string to bytes, if needed
bcmd = cmd if type(cmd) is bytes else cmd.encode()
bcmd += b'\r'
# Send command to the modem
log.debug('Sending AT command: %s' % cmd)
try:
wlen = self._sl.write(bcmd)
assert(wlen == len(bcmd))
except:
raise ReaderError('Failed to send AT command: %s' % cmd)
# Give the modem some time...
time.sleep(0.3)
# Read the response
try:
# Skip characters sent back
self._sl.read(wlen)
# Read the rest
rsp = self._sl.read_all()
# Strip '\r\n'
rsp = rsp.strip()
# Split into a list
rsp = rsp.split(b'\r\n\r\n')
except:
raise ReaderError('Failed parse response to AT command: %s' % cmd)
log.debug('Got response from modem: %s' % rsp)
return rsp
def reset_card(self):
# Make sure that we can talk to the modem
if self.send_at_cmd('AT') != [b'OK']:
raise ReaderError('Failed to connect to modem')
# Reset the modem, just to be sure
if self.send_at_cmd('ATZ') != [b'OK']:
raise ReaderError('Failed to reset the modem')
# Make sure that generic SIM access is supported
if self.send_at_cmd('AT+CSIM=?') != [b'OK']:
raise ReaderError('The modem does not seem to support SIM access')
log.info('Modem at \'%s\' is ready!' % self._device)
def connect(self):
pass # Nothing to do really ...
def disconnect(self):
pass # Nothing to do really ...
def wait_for_card(self, timeout=None, newcardonly=False):
pass # Nothing to do really ...
def send_apdu_raw(self, pdu):
# Prepare the command as described in 8.17
cmd = 'AT+CSIM=%d,\"%s\"' % (len(pdu), pdu)
# Send AT+CSIM command to the modem
# TODO: also handle +CME ERROR: <err>
rsp = self.send_at_cmd(cmd)
if len(rsp) != 2 or rsp[-1] != b'OK':
raise ReaderError('APDU transfer failed: %s' % str(rsp))
rsp = rsp[0] # Get rid of b'OK'
# Make sure that the response has format: b'+CSIM: %d,\"%s\"'
try:
result = re.match(b'\+CSIM: (\d+),\"([0-9A-F]+)\"', rsp)
(rsp_pdu_len, rsp_pdu) = result.groups()
except:
raise ReaderError('Failed to parse response from modem: %s' % rsp)
# TODO: make sure we have at least SW
data = rsp_pdu[:-4].decode()
sw = rsp_pdu[-4:].decode()
return data, sw

View File

@@ -22,7 +22,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from smartcard.CardConnection import CardConnection
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException
from smartcard.System import readers
@@ -53,10 +52,7 @@ class PcscSimLink(LinkBase):
def connect(self):
try:
# Explicitly select T=0 communication protocol
self._con.connect(CardConnection.T0_protocol)
except CardConnectionException:
raise ProtocolError()
self._con.connect()
except NoCardException:
raise NoCardError()
@@ -67,8 +63,11 @@ class PcscSimLink(LinkBase):
self._con.disconnect()
def reset_card(self):
self.disconnect()
self.connect()
self._con.disconnect()
try:
self._con.connect()
except NoCardException:
raise NoCardError()
return 1
def send_apdu_raw(self, pdu):

View File

@@ -143,26 +143,26 @@ class SerialSimLink(LinkBase):
for i in range(4):
if t0 & (0x10 << i):
b = self._rx_byte()
self._atr.append(ord(b))
self._atr.apend(ord(b))
self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
for i in range(0, t0 & 0xf):
b = self._rx_byte()
self._atr.append(ord(b))
self._atr.apend(ord(b))
self._dbg_print("Historical = %x" % ord(b))
while True:
x = self._rx_byte()
if not x:
break
self._atr.append(ord(x))
self._atr.apend(ord(x))
self._dbg_print("Extra: %x" % ord(x))
return 1
def _dbg_print(self, s):
if self._debug:
print(s)
print s
def _tx_byte(self, b):
self._sl.write(b)
@@ -213,7 +213,7 @@ class SerialSimLink(LinkBase):
self._tx_string(pdu[5:])
# Receive data (including SW !)
# length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
# length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1/2) ]
to_recv = data_len - len(pdu) + 5 + 2
data = ''

View File

@@ -1,248 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Various constants from ETSI TS 131 102
"""
#
# Copyright (C) 2020 Supreeth Herle <herlesupreeth@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Mapping between USIM Service Number and its description
EF_UST_map = {
1: 'Local Phone Book',
2: 'Fixed Dialling Numbers (FDN)',
3: 'Extension 2',
4: 'Service Dialling Numbers (SDN)',
5: 'Extension3',
6: 'Barred Dialling Numbers (BDN)',
7: 'Extension4',
8: 'Outgoing Call Information (OCI and OCT)',
9: 'Incoming Call Information (ICI and ICT)',
10: 'Short Message Storage (SMS)',
11: 'Short Message Status Reports (SMSR)',
12: 'Short Message Service Parameters (SMSP)',
13: 'Advice of Charge (AoC)',
14: 'Capability Configuration Parameters 2 (CCP2)',
15: 'Cell Broadcast Message Identifier',
16: 'Cell Broadcast Message Identifier Ranges',
17: 'Group Identifier Level 1',
18: 'Group Identifier Level 2',
19: 'Service Provider Name',
20: 'User controlled PLMN selector with Access Technology',
21: 'MSISDN',
22: 'Image (IMG)',
23: 'Support of Localised Service Areas (SoLSA)',
24: 'Enhanced Multi-Level Precedence and Pre-emption Service',
25: 'Automatic Answer for eMLPP',
26: 'RFU',
27: 'GSM Access',
28: 'Data download via SMS-PP',
29: 'Data download via SMS-CB',
30: 'Call Control by USIM',
31: 'MO-SMS Control by USIM',
32: 'RUN AT COMMAND command',
33: 'shall be set to 1',
34: 'Enabled Services Table',
35: 'APN Control List (ACL)',
36: 'Depersonalisation Control Keys',
37: 'Co-operative Network List',
38: 'GSM security context',
39: 'CPBCCH Information',
40: 'Investigation Scan',
41: 'MexE',
42: 'Operator controlled PLMN selector with Access Technology',
43: 'HPLMN selector with Access Technology',
44: 'Extension 5',
45: 'PLMN Network Name',
46: 'Operator PLMN List',
47: 'Mailbox Dialling Numbers',
48: 'Message Waiting Indication Status',
49: 'Call Forwarding Indication Status',
50: 'Reserved and shall be ignored',
51: 'Service Provider Display Information',
52: 'Multimedia Messaging Service (MMS)',
53: 'Extension 8',
54: 'Call control on GPRS by USIM',
55: 'MMS User Connectivity Parameters',
56: 'Network\'s indication of alerting in the MS (NIA)',
57: 'VGCS Group Identifier List (EFVGCS and EFVGCSS)',
58: 'VBS Group Identifier List (EFVBS and EFVBSS)',
59: 'Pseudonym',
60: 'User Controlled PLMN selector for I-WLAN access',
61: 'Operator Controlled PLMN selector for I-WLAN access',
62: 'User controlled WSID list',
63: 'Operator controlled WSID list',
64: 'VGCS security',
65: 'VBS security',
66: 'WLAN Reauthentication Identity',
67: 'Multimedia Messages Storage',
68: 'Generic Bootstrapping Architecture (GBA)',
69: 'MBMS security',
70: 'Data download via USSD and USSD application mode',
71: 'Equivalent HPLMN',
72: 'Additional TERMINAL PROFILE after UICC activation',
73: 'Equivalent HPLMN Presentation Indication',
74: 'Last RPLMN Selection Indication',
75: 'OMA BCAST Smart Card Profile',
76: 'GBA-based Local Key Establishment Mechanism',
77: 'Terminal Applications',
78: 'Service Provider Name Icon',
79: 'PLMN Network Name Icon',
80: 'Connectivity Parameters for USIM IP connections',
81: 'Home I-WLAN Specific Identifier List',
82: 'I-WLAN Equivalent HPLMN Presentation Indication',
83: 'I-WLAN HPLMN Priority Indication',
84: 'I-WLAN Last Registered PLMN',
85: 'EPS Mobility Management Information',
86: 'Allowed CSG Lists and corresponding indications',
87: 'Call control on EPS PDN connection by USIM',
88: 'HPLMN Direct Access',
89: 'eCall Data',
90: 'Operator CSG Lists and corresponding indications',
91: 'Support for SM-over-IP',
92: 'Support of CSG Display Control',
93: 'Communication Control for IMS by USIM',
94: 'Extended Terminal Applications',
95: 'Support of UICC access to IMS',
96: 'Non-Access Stratum configuration by USIM',
97: 'PWS configuration by USIM',
98: 'RFU',
99: 'URI support by UICC',
100: 'Extended EARFCN support',
101: 'ProSe',
102: 'USAT Application Pairing',
103: 'Media Type support',
104: 'IMS call disconnection cause',
105: 'URI support for MO SHORT MESSAGE CONTROL',
106: 'ePDG configuration Information support',
107: 'ePDG configuration Information configured',
108: 'ACDC support',
109: 'MCPTT',
110: 'ePDG configuration Information for Emergency Service support',
111: 'ePDG configuration Information for Emergency Service configured',
}
LOCI_STATUS_map = {
0: 'updated',
1: 'not updated',
2: 'plmn not allowed',
3: 'locatation area not allowed'
}
EF_USIM_ADF_map = {
'LI': '6F05',
'ARR': '6F06',
'IMSI': '6F07',
'Keys': '6F08',
'KeysPS': '6F09',
'DCK': '6F2C',
'HPPLMN': '6F31',
'CNL': '6F32',
'ACMmax': '6F37',
'UST': '6F38',
'ACM': '6F39',
'FDN': '6F3B',
'SMS': '6F3C',
'GID1': '6F3E',
'GID2': '6F3F',
'MSISDN': '6F40',
'PUCT': '6F41',
'SMSP': '6F42',
'SMSS': '6F42',
'CBMI': '6F45',
'SPN': '6F46',
'SMSR': '6F47',
'CBMID': '6F48',
'SDN': '6F49',
'EXT2': '6F4B',
'EXT3': '6F4C',
'BDN': '6F4D',
'EXT5': '6F4E',
'CCP2': '6F4F',
'CBMIR': '6F50',
'EXT4': '6F55',
'EST': '6F56',
'ACL': '6F57',
'CMI': '6F58',
'START-HFN': '6F5B',
'THRESHOLD': '6F5C',
'PLMNwAcT': '6F60',
'OPLMNwAcT': '6F61',
'HPLMNwAcT': '6F62',
'PSLOCI': '6F73',
'ACC': '6F78',
'FPLMN': '6F7B',
'LOCI': '6F7E',
'ICI': '6F80',
'OCI': '6F81',
'ICT': '6F82',
'OCT': '6F83',
'AD': '6FAD',
'VGCS': '6FB1',
'VGCSS': '6FB2',
'VBS': '6FB3',
'VBSS': '6FB4',
'eMLPP': '6FB5',
'AAeM': '6FB6',
'ECC': '6FB7',
'Hiddenkey': '6FC3',
'NETPAR': '6FC4',
'PNN': '6FC5',
'OPL': '6FC6',
'MBDN': '6FC7',
'EXT6': '6FC8',
'MBI': '6FC9',
'MWIS': '6FCA',
'CFIS': '6FCB',
'EXT7': '6FCC',
'SPDI': '6FCD',
'MMSN': '6FCE',
'EXT8': '6FCF',
'MMSICP': '6FD0',
'MMSUP': '6FD1',
'MMSUCP': '6FD2',
'NIA': '6FD3',
'VGCSCA': '6FD4',
'VBSCA': '6FD5',
'GBAP': '6FD6',
'MSK': '6FD7',
'MUK': '6FD8',
'EHPLMN': '6FD9',
'GBANL': '6FDA',
'EHPLMNPI': '6FDB',
'LRPLMNSI': '6FDC',
'NAFKCA': '6FDD',
'SPNI': '6FDE',
'PNNI': '6FDF',
'NCP-IP': '6FE2',
'EPSLOCI': '6FE3',
'EPSNSC': '6FE4',
'UFC': '6FE6',
'UICCIARI': '6FE7',
'NASCONFIG': '6FE8',
'PWC': '6FEC',
'FDNURI': '6FED',
'BDNURI': '6FEE',
'SDNURI': '6FEF',
'IWL': '6FF0',
'IPS': '6FF1',
'IPD': '6FF2',
'ePDGId': '6FF3',
'ePDGSelection': '6FF4',
'ePDGIdEm': '6FF5',
'ePDGSelectionEm': '6FF6',
}

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Various constants from ETSI TS 131 103 V14.2.0
"""
#
# Copyright (C) 2020 Supreeth Herle <herlesupreeth@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Mapping between ISIM Service Number and its description
EF_IST_map = {
1: 'P-CSCF address',
2: 'Generic Bootstrapping Architecture (GBA)',
3: 'HTTP Digest',
4: 'GBA-based Local Key Establishment Mechanism',
5: 'Support of P-CSCF discovery for IMS Local Break Out',
6: 'Short Message Storage (SMS)',
7: 'Short Message Status Reports (SMSR)',
8: 'Support for SM-over-IP including data download via SMS-PP as defined in TS 31.111 [31]',
9: 'Communication Control for IMS by ISIM',
10: 'Support of UICC access to IMS',
11: 'URI support by UICC',
12: 'Media Type support',
13: 'IMS call disconnection cause',
14: 'URI support for MO SHORT MESSAGE CONTROL',
15: 'MCPTT',
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'
}

View File

@@ -48,7 +48,6 @@ EF_num = {
# MF
'ICCID': '2FE2',
'ELP': '2F05',
'DIR': '2F00',
# DF_TELECOM
'ADN': '6F3A',
@@ -165,7 +164,6 @@ DF = {
EF = {
'ICCID': [MF_num, EF_num['ICCID']],
'ELP': [MF_num, EF_num['ELP']],
'DIR': [MF_num, EF_num['DIR']],
'ADN': DF['TELECOM']+[EF_num['ADN']],
'FDN': DF['TELECOM']+[EF_num['FDN']],
@@ -231,7 +229,7 @@ EF = {
'SUME': DF['GSM']+[EF_num['SUME']],
'PLMNwAcT': DF['GSM']+[EF_num['PLMNwAcT']],
'OPLMNwAcT': DF['GSM']+[EF_num['OPLMNwAcT']],
# Figure 8 names it HPLMNAcT, but in the text it's names it HPLMNwAcT
# Figure 8 names it HPLMNAcT, but in the text it's named HPLMNwAcT
'HPLMNAcT': DF['GSM']+[EF_num['HPLMNAcT']],
'HPLMNwAcT': DF['GSM']+[EF_num['HPLMNAcT']],
'CPBCCH': DF['GSM']+[EF_num['CPBCCH']],
@@ -251,66 +249,3 @@ EF = {
'MMSUP': DF['GSM']+[EF_num['MMSUP']],
'MMSUCP': DF['GSM']+[EF_num['MMSUCP']],
}
# Mapping between SIM Service Number and its description
EF_SST_map = {
1: 'CHV1 disable function',
2: 'Abbreviated Dialling Numbers (ADN)',
3: 'Fixed Dialling Numbers (FDN)',
4: 'Short Message Storage (SMS)',
5: 'Advice of Charge (AoC)',
6: 'Capability Configuration Parameters (CCP)',
7: 'PLMN selector',
8: 'RFU',
9: 'MSISDN',
10: 'Extension1',
11: 'Extension2',
12: 'SMS Parameters',
13: 'Last Number Dialled (LND)',
14: 'Cell Broadcast Message Identifier',
15: 'Group Identifier Level 1',
16: 'Group Identifier Level 2',
17: 'Service Provider Name',
18: 'Service Dialling Numbers (SDN)',
19: 'Extension3',
20: 'RFU',
21: 'VGCS Group Identifier List (EFVGCS and EFVGCSS)',
22: 'VBS Group Identifier List (EFVBS and EFVBSS)',
23: 'enhanced Multi-Level Precedence and Pre-emption Service',
24: 'Automatic Answer for eMLPP',
25: 'Data download via SMS-CB',
26: 'Data download via SMS-PP',
27: 'Menu selection',
28: 'Call control',
29: 'Proactive SIM',
30: 'Cell Broadcast Message Identifier Ranges',
31: 'Barred Dialling Numbers (BDN)',
32: 'Extension4',
33: 'De-personalization Control Keys',
34: 'Co-operative Network List',
35: 'Short Message Status Reports',
36: 'Network\'s indication of alerting in the MS',
37: 'Mobile Originated Short Message control by SIM',
38: 'GPRS',
39: 'Image (IMG)',
40: 'SoLSA (Support of Local Service Area)',
41: 'USSD string data object supported in Call Control',
42: 'RUN AT COMMAND command',
43: 'User controlled PLMN Selector with Access Technology',
44: 'Operator controlled PLMN Selector with Access Technology',
45: 'HPLMN Selector with Access Technology',
46: 'CPBCCH Information',
47: 'Investigation Scan',
48: 'Extended Capability Configuration Parameters',
49: 'MExE',
50: 'Reserved and shall be ignored',
51: 'PLMN Network Name',
52: 'Operator PLMN List',
53: 'Mailbox Dialling Numbers',
54: 'Message Waiting Indication Status',
55: 'Call Forwarding Indication Status',
56: 'Service Provider Display Information',
57: 'Multimedia Messaging Service (MMS)',
58: 'Extension 8',
59: 'MMS User Connectivity Parameters',
}

View File

@@ -35,16 +35,11 @@ def i2h(s):
return ''.join(['%02x'%(x) for x in s])
def h2s(s):
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])
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2]) if not (x == 'f' and y == 'f') ])
def s2h(s):
return b2h(s)
# List of bytes to string
def i2s(s):
return ''.join([chr(x) for x in s])
def swap_nibbles(s):
return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
@@ -54,29 +49,11 @@ def rpad(s, l, c='f'):
def lpad(s, l, c='f'):
return c * (l - len(s)) + s
def half_round_up(n):
return (n + 1)//2
# IMSI encoded format:
# For IMSI 0123456789ABCDE:
#
# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
#
# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
#
# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
# encode itself.
#
# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
# even length IMSI only uses half of the last byte.
def enc_imsi(imsi):
"""Converts a string imsi into the value of the EF"""
l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
l = (len(imsi) + 1) // 2 # Required bytes
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(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
return ei
def dec_imsi(ef):
@@ -84,17 +61,13 @@ def dec_imsi(ef):
if len(ef) < 4:
return None
l = int(ef[0:2], 16) * 2 # Length of the IMSI string
l = l - 1 # Encoded length byte includes oe nibble
swapped = swap_nibbles(ef[2:]).rstrip('f')
if len(swapped) < 1:
return None
swapped = swap_nibbles(ef[2:])
oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
if not oe:
# if even, only half of last byte was used
if oe:
l = l-1
if l != len(swapped) - 1:
if l+1 > len(swapped):
return None
imsi = swapped[1:]
imsi = swapped[1:l+2]
return imsi
def dec_iccid(ef):
@@ -105,9 +78,7 @@ def enc_iccid(iccid):
def enc_plmn(mcc, mnc):
"""Converts integer MCC/MNC into 3 bytes for EF"""
if len(mnc) == 2:
mnc = "F%s" % mnc
return swap_nibbles("%s%s" % (mcc, mnc))
return swap_nibbles(lpad('%03d' % mcc, 3) + lpad('%02d' % mnc, 3))
def dec_spn(ef):
byte1 = int(ef[0:2])
@@ -122,119 +93,6 @@ def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
if oplmn_disp: byte1 = byte1|0x02
return i2h([byte1])+s2h(name)
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):
ia = h2i(plmn)
digit1 = ia[0] & 0x0F # 1st byte, LSB
digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
digit3 = ia[1] & 0x0F # 2nd byte, LSB
if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
return 0xFFF # 4095
return derive_mcc(digit1, digit2, digit3)
def dec_mnc_from_plmn(plmn):
ia = h2i(plmn)
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)
def dec_act(twohexbytes):
act_list = [
{'bit': 15, 'name': "UTRAN"},
{'bit': 14, 'name': "E-UTRAN"},
{'bit': 7, 'name': "GSM"},
{'bit': 6, 'name': "GSM COMPACT"},
{'bit': 5, 'name': "cdma2000 HRPD"},
{'bit': 4, 'name': "cdma2000 1xRTT"},
]
ia = h2i(twohexbytes)
u16t = (ia[0] << 8)|ia[1]
sel = []
for a in act_list:
if u16t & (1 << a['bit']):
sel.append(a['name'])
return sel
def dec_xplmn_w_act(fivehexbytes):
res = {'mcc': 0, 'mnc': 0, 'act': []}
plmn_chars = 6
act_chars = 4
plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
res['mcc'] = dec_mcc_from_plmn(plmn_str)
res['mnc'] = dec_mnc_from_plmn(plmn_str)
res['act'] = dec_act(act_str)
return res
def format_xplmn_w_act(hexstr):
s = ""
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"
else:
rec_str = "MCC: %03d MNC: %03d AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
s += "\t%s # %s\n" % (rec_data, rec_str)
return s
def dec_loci(hexstr):
res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
res['tmsi'] = hexstr[:8]
res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
res['lac'] = hexstr[14:18]
res['status'] = h2i(hexstr[20:22])
return res
def dec_psloci(hexstr):
res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
res['p-tmsi'] = hexstr[:8]
res['p-tmsi-sig'] = hexstr[8:14]
res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
res['lac'] = hexstr[20:24]
res['rac'] = hexstr[24:26]
res['status'] = h2i(hexstr[26:28])
return res
def dec_epsloci(hexstr):
res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
res['guti'] = hexstr[:24]
res['tai'] = hexstr[24:34]
res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
res['tac'] = hexstr[30:34]
res['status'] = h2i(hexstr[34:36])
return res
def dec_xplmn(threehexbytes):
res = {'mcc': 0, 'mnc': 0, 'act': []}
plmn_chars = 6
plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
res['mcc'] = dec_mcc_from_plmn(plmn_str)
res['mnc'] = dec_mnc_from_plmn(plmn_str)
return res
def format_xplmn(hexstr):
s = ""
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"
else:
rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
s += "\t%s # %s\n" % (rec_data, rec_str)
return s
def derive_milenage_opc(ki_hex, op_hex):
"""
Run the milenage algorithm to calculate OPC from Ki and OP
@@ -256,316 +114,21 @@ def calculate_luhn(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
def mcc_from_imsi(imsi):
"""
Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
"""
if imsi == None:
return None
if len(imsi) > 3:
return imsi[:3]
else:
return None
def mnc_from_imsi(imsi, long=False):
"""
Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
"""
if imsi == None:
return None
if len(imsi) > 3:
if long:
return imsi[3:6]
else:
return imsi[3:5]
else:
return None
def derive_mcc(digit1, digit2, digit3):
"""
Derive decimal representation of the MCC (Mobile Country Code)
from three given digits.
"""
mcc = 0
if digit1 != 0x0f:
mcc += digit1 * 100
if digit2 != 0x0f:
mcc += digit2 * 10
if digit3 != 0x0f:
mcc += digit3
return mcc
def derive_mnc(digit1, digit2, digit3=0x0f):
"""
Derive decimal representation of the MNC (Mobile Network Code)
from two or (optionally) three given digits.
"""
mnc = 0
# 3-rd digit is optional for the MNC. If present
# the algorythm is the same as for the MCC.
if digit3 != 0x0f:
return derive_mcc(digit1, digit2, digit3)
if digit1 != 0x0f:
mnc += digit1 * 10
if digit2 != 0x0f:
mnc += digit2
return mnc
def dec_msisdn(ef_msisdn):
"""
Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
"""
# Convert from str to (kind of) 'bytes'
ef_msisdn = h2b(ef_msisdn)
# Make sure mandatory fields are present
if len(ef_msisdn) < 14:
raise ValueError("EF.MSISDN is too short")
# Skip optional Alpha Identifier
xlen = len(ef_msisdn) - 14
msisdn_lhv = ef_msisdn[xlen:]
# Parse the length (in bytes) of the BCD encoded number
bcd_len = ord(msisdn_lhv[0])
# BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
if bcd_len == 0xff:
return None
elif bcd_len > 11 or bcd_len < 1:
raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
# Parse ToN / NPI
ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
npi = ord(msisdn_lhv[1]) & 0x0f
bcd_len -= 1
# No MSISDN?
if not bcd_len:
return (npi, ton, None)
msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
# International number 10.5.118/3GPP TS 24.008
if ton == 0x01:
msisdn = '+' + msisdn
return (npi, ton, msisdn)
def enc_msisdn(msisdn, npi=0x01, ton=0x03):
"""
Encode MSISDN as LHV so it can be stored to EF.MSISDN.
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
Default NPI / ToN values:
- NPI: ISDN / telephony numbering plan (E.164 / E.163),
- ToN: network specific or international number (if starts with '+').
"""
# Leading '+' indicates International Number
if msisdn[0] == '+':
msisdn = msisdn[1:]
ton = 0x01
# Append 'f' padding if number of digits is odd
if len(msisdn) % 2 > 0:
msisdn += 'f'
# BCD length also includes NPI/ToN header
bcd_len = len(msisdn) // 2 + 1
npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
def dec_st(st, table="sim"):
"""
Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
"""
if table == "isim":
from pySim.ts_31_103 import EF_IST_map
lookup_map = EF_IST_map
elif table == "usim":
from pySim.ts_31_102 import EF_UST_map
lookup_map = EF_UST_map
else:
from pySim.ts_51_011 import EF_SST_map
lookup_map = EF_SST_map
st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
avail_st = ""
# Get each byte and check for available services
for i in range(0, len(st_bytes)):
# Byte i contains info about Services num (8i+1) to num (8i+8)
byte = int(st_bytes[i], 16)
# Services in each byte are in order MSB to LSB
# MSB - Service (8i+8)
# LSB - Service (8i+1)
for j in range(1, 9):
if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
# Byte X contains info about Services num (8X-7) to num (8X)
# bit = 1: service available
# bit = 0: service not available
avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
byte = byte >> 1
return avail_st
def first_TLV_parser(bytelist):
def dec_select_ef_response(response):
'''
first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
parses first TLV format record in a list of bytelist
returns a 3-Tuple: Tag, Length, Value
Value is a list of bytes
parsing of length is ETSI'style 101.220
As defined in the TS 151.011 9.2.1 SELECT
'''
Tag = bytelist[0]
if bytelist[1] == 0xFF:
Len = bytelist[2]*256 + bytelist[3]
Val = bytelist[4:4+Len]
length = int(response[4:8], 16)
file_id = response[8:12]
file_type = response[12:14]
increase_cmd = response[14:16]
access_cond = response[16:22]
file_status = response[22:24]
data_len = int(response[24:26], 16)
ef_struct = response[26:28]
if len(response) >= 30:
record_len = int(response[28:30], 16)
else:
Len = bytelist[1]
Val = bytelist[2:2+Len]
return (Tag, Len, Val)
def TLV_parser(bytelist):
'''
TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
loops on the input list of bytes with the "first_TLV_parser()" function
returns a list of 3-Tuples
'''
ret = []
while len(bytelist) > 0:
T, L, V = first_TLV_parser(bytelist)
if T == 0xFF:
# padding bytes
break
ret.append( (T, L, V) )
# need to manage length of L
if L > 0xFE:
bytelist = bytelist[ L+4 : ]
else:
bytelist = bytelist[ L+2 : ]
return ret
def dec_epdgid(hexstr):
"""
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
epdgid_bytes = h2i(hexstr)
s = ""
# Get list of tuples containing parsed TLVs
tlvs = TLV_parser(epdgid_bytes)
for tlv in tlvs:
# tlv = (T, L, [V])
# T = Tag
# L = Length
# [V] = List of value
# Invalid Tag value scenario
if tlv[0] != 0x80:
continue
# Empty field - Zero length
if tlv[1] == 0:
continue
# First byte in the value has the address type
addr_type = tlv[2][0]
# 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))
return s
def enc_epdgid(epdg_addr, addr_type='00'):
"""
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 ePDG Address
"""
s = ""
# 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
return s
def sanitize_pin_adm(opts):
"""
The ADM pin can be supplied either in its hexadecimal form or as
ascii string. This function checks the supplied opts parameter and
returns the pin_adm as hex encoded string, regardles in which form
it was originally supplied by the user
"""
pin_adm = None
if opts.pin_adm is not None:
if len(opts.pin_adm) <= 8:
pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
pin_adm = rpad(pin_adm, 16)
else:
raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
if opts.pin_adm_hex is not None:
if len(opts.pin_adm_hex) == 16:
pin_adm = opts.pin_adm_hex
# Ensure that it's hex-encoded
try:
try_encode = h2b(pin_adm)
except ValueError:
raise ValueError("PIN-ADM needs to be hex encoded using this option")
else:
raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
return pin_adm
def init_reader(opts):
"""
Init card reader driver
"""
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
record_len = 0
return (length, file_id, file_type, increase_cmd, access_cond, file_status, data_len, ef_struct, record_len)

View File

@@ -1,76 +0,0 @@
#!/usr/bin/pyton
import unittest
import utils
class DecTestCase(unittest.TestCase):
def testSplitHexStringToListOf5ByteEntries(self):
input_str = "ffffff0003ffffff0002ffffff0001"
expected = [
"ffffff0003",
"ffffff0002",
"ffffff0001",
]
self.assertEqual(utils.hexstr_to_fivebytearr(input_str), expected)
def testDecMCCfromPLMN(self):
self.assertEqual(utils.dec_mcc_from_plmn("92f501"), 295)
def testDecMCCfromPLMN_unused(self):
self.assertEqual(utils.dec_mcc_from_plmn("ff0f00"), 4095)
def testDecMNCfromPLMN_twoDigitMNC(self):
self.assertEqual(utils.dec_mnc_from_plmn("92f501"), 10)
def testDecMNCfromPLMN_threeDigitMNC(self):
self.assertEqual(utils.dec_mnc_from_plmn("031263"), 361)
def testDecMNCfromPLMN_unused(self):
self.assertEqual(utils.dec_mnc_from_plmn("00f0ff"), 4095)
def testDecAct_noneSet(self):
self.assertEqual(utils.dec_act("0000"), [])
def testDecAct_onlyUtran(self):
self.assertEqual(utils.dec_act("8000"), ["UTRAN"])
def testDecAct_onlyEUtran(self):
self.assertEqual(utils.dec_act("4000"), ["E-UTRAN"])
def testDecAct_onlyGsm(self):
self.assertEqual(utils.dec_act("0080"), ["GSM"])
def testDecAct_onlyGsmCompact(self):
self.assertEqual(utils.dec_act("0040"), ["GSM COMPACT"])
def testDecAct_onlyCdma2000HRPD(self):
self.assertEqual(utils.dec_act("0020"), ["cdma2000 HRPD"])
def testDecAct_onlyCdma20001xRTT(self):
self.assertEqual(utils.dec_act("0010"), ["cdma2000 1xRTT"])
def testDecAct_allSet(self):
self.assertEqual(utils.dec_act("ffff"), ["UTRAN", "E-UTRAN", "GSM", "GSM COMPACT", "cdma2000 HRPD", "cdma2000 1xRTT"])
def testDecxPlmn_w_act(self):
expected = {'mcc': 295, 'mnc': 10, 'act': ["UTRAN"]}
self.assertEqual(utils.dec_xplmn_w_act("92f5018000"), expected)
def testFormatxPlmn_w_act(self):
input_str = "92f501800092f5508000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000"
expected = '''92f5018000 # MCC: 295 MNC: 10 AcT: UTRAN
92f5508000 # MCC: 295 MNC: 5 AcT: UTRAN
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
'''
self.assertEqual(utils.format_xplmn_w_act(input_str), expected)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,5 +0,0 @@
MCC=001
MNC=01
IMSI=001010000000111
ADM_HEX=CAE743DB9C5B5A58

View File

@@ -1,119 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: Fairwaves-SIM
ICCID: 8988219000000117833
IMSI: 001010000000111
GID1: ffffffffffffffff
GID2: ffffffffffffffff
SMSP: e1ffffffffffffffffffffffff0581005155f5ffffffffffff000000ffffffffffffffffffffffffffff
SPN: Fairwaves
Display HPLMN: False
Display OPLMN: False
PLMNsel: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ACC: 0008
MSISDN: Not available
AD: 00000002
SIM Service Table: ff3cc3ff030fff0f000fff03f0c0
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 29 - Proactive SIM
Service 30 - Cell Broadcast Message Identifier Ranges
Service 31 - Barred Dialling Numbers (BDN)
Service 32 - Extension4
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 41 - USSD string data object supported in Call Control
Service 42 - RUN AT COMMAND command
Service 43 - User controlled PLMN Selector with Access Technology
Service 44 - Operator controlled PLMN Selector with Access Technology
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 55 - Call Forwarding Indication Status
Service 56 - Service Provider Display Information
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
USIM Service Table: 01ea1ffc21360480010000
Service 1 - Local Phone Book
Service 10 - Short Message Storage (SMS)
Service 12 - Short Message Service Parameters (SMSP)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 16 - Cell Broadcast Message Identifier Ranges
Service 17 - Group Identifier Level 1
Service 18 - Group Identifier Level 2
Service 19 - Service Provider Name
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 30 - Call Control by USIM
Service 31 - MO-SMS Control by USIM
Service 32 - RUN AT COMMAND command
Service 33 - shall be set to 1
Service 38 - GSM security context
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 51 - Service Provider Display Information
Service 64 - VGCS security
Service 65 - VBS security
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -1,5 +0,0 @@
MCC=001
MNC=01
IMSI=001010000000102
ADM_HEX=15E31383624FDC8A

View File

@@ -1,135 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: Wavemobile-SIM
ICCID: 89445310150011013678
IMSI: 001010000000102
GID1: Can't read file -- SW match failed! Expected 9000 and got 6a82.
GID2: Can't read file -- SW match failed! Expected 9000 and got 6a82.
SMSP: e1ffffffffffffffffffffffff0581005155f5ffffffffffff000000ffffffffffffffffffffffffffff
SPN: wavemobile
Display HPLMN: False
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 6a82.
ACC: abce
MSISDN: Not available
AD: 00ffff02
SIM Service Table: ff33ff0f3c00ff0f000cf0c0f0030000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 37 - Mobile Originated Short Message control by SIM
Service 38 - GPRS
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 55 - Call Forwarding Indication Status
Service 56 - Service Provider Display Information
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
USIM Service Table: 9eff1b3c37fe5900000000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
Service 5 - Extension3
Service 8 - Outgoing Call Information (OCI and OCT)
Service 9 - Incoming Call Information (ICI and ICT)
Service 10 - Short Message Storage (SMS)
Service 11 - Short Message Status Reports (SMSR)
Service 12 - Short Message Service Parameters (SMSP)
Service 13 - Advice of Charge (AoC)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 16 - Cell Broadcast Message Identifier Ranges
Service 17 - Group Identifier Level 1
Service 18 - Group Identifier Level 2
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 30 - Call Control by USIM
Service 33 - shall be set to 1
Service 34 - Enabled Services Table
Service 35 - APN Control List (ACL)
Service 37 - Co-operative Network List
Service 38 - GSM security context
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 44 - Extension 5
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 47 - Mailbox Dialling Numbers
Service 48 - Message Waiting Indication Status
Service 49 - Call Forwarding Indication Status
Service 52 - Multimedia Messaging Service (MMS)
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

@@ -1,6 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102

View File

@@ -1,57 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: fakemagicsim
Can't read AIDs from SIM -- SW match failed! Expected 9000 and got 9404.
ICCID: 1122334455667788990
IMSI: 001010000000102
GID1: Can't read file -- SW match failed! Expected 9000 and got 9404.
GID2: Can't read file -- SW match failed! Expected 9000 and got 9404.
SMSP: ffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Magic
Display HPLMN: True
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
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
AD: 000000
SIM Service Table: ff3fff0f0300f003000c
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 55 - Call Forwarding Indication Status
Service 56 - Service Provider Display Information
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Done !

View File

@@ -1,7 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=AABBCCDDEEFFAABBCCDDEEFFAABBCCDD
OPC=12345678901234567890123456789012
IMSI=001010000000102
ADM=72273953

View File

@@ -1,169 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmoISIM-SJA2
ICCID: 8988211900000000004
IMSI: 001010000000102
GID1: ffffffffffffffffffff
GID2: ffffffffffffffffffff
SMSP: ffffffffffffffffffffffffffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Not available
Display HPLMN: False
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
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)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 29 - Proactive SIM
Service 30 - Cell Broadcast Message Identifier Ranges
Service 31 - Barred Dialling Numbers (BDN)
Service 32 - Extension4
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 37 - Mobile Originated Short Message control by SIM
Service 38 - GPRS
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
EHPLMN:
00f110 # MCC: 001 MNC: 001
ffffff # unused
ffffff # unused
ffffff # unused
USIM Service Table: beff9f9de73e0408400170730000002e00000000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
Service 5 - Extension3
Service 6 - Barred Dialling Numbers (BDN)
Service 8 - Outgoing Call Information (OCI and OCT)
Service 9 - Incoming Call Information (ICI and ICT)
Service 10 - Short Message Storage (SMS)
Service 11 - Short Message Status Reports (SMSR)
Service 12 - Short Message Service Parameters (SMSP)
Service 13 - Advice of Charge (AoC)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 16 - Cell Broadcast Message Identifier Ranges
Service 17 - Group Identifier Level 1
Service 18 - Group Identifier Level 2
Service 19 - Service Provider Name
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 24 - Enhanced Multi-Level Precedence and Pre-emption Service
Service 25 - Automatic Answer for eMLPP
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 32 - RUN AT COMMAND command
Service 33 - shall be set to 1
Service 34 - Enabled Services Table
Service 35 - APN Control List (ACL)
Service 38 - GSM security context
Service 39 - CPBCCH Information
Service 40 - Investigation Scan
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 44 - Extension 5
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 51 - Service Provider Display Information
Service 60 - User Controlled PLMN selector for I-WLAN access
Service 71 - Equivalent HPLMN
Service 73 - Equivalent HPLMN Presentation Indication
Service 85 - EPS Mobility Management Information
Service 86 - Allowed CSG Lists and corresponding indications
Service 87 - Call control on EPS PDN connection by USIM
Service 89 - eCall Data
Service 90 - Operator CSG Lists and corresponding indications
Service 93 - Communication Control for IMS by USIM
Service 94 - Extended Terminal Applications
Service 95 - Support of UICC access to IMS
ePDGId:
Not available
ISIM Service Table: 190200
Service 1 - P-CSCF address
Service 4 - GBA-based Local Key Establishment Mechanism
Service 5 - Support of P-CSCF discovery for IMS Local Break Out
Service 10 - Support of UICC access to IMS
Done !

View File

@@ -1,8 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
MSISDN=+77776336143
ADM=55538407

View File

@@ -1,143 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmoUSIM-SJS1
ICCID: 1122334455667788990
IMSI: 001010000000102
GID1: ffffffffffffffffffff
GID2: ffffffffffffffffffff
SMSP: ffffffffffffffffffffffffffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Magic
Display HPLMN: True
Display OPLMN: True
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ACC: 0008
MSISDN (NPI=1 ToN=1): +77776336143
AD: 00000002
SIM Service Table: ff3fffff3f003f1ff00c00c0f00000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 29 - Proactive SIM
Service 30 - Cell Broadcast Message Identifier Ranges
Service 31 - Barred Dialling Numbers (BDN)
Service 32 - Extension4
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 37 - Mobile Originated Short Message control by SIM
Service 38 - GPRS
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
USIM Service Table: 9e6b1dfc67f6580000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
Service 5 - Extension3
Service 8 - Outgoing Call Information (OCI and OCT)
Service 9 - Incoming Call Information (ICI and ICT)
Service 10 - Short Message Storage (SMS)
Service 12 - Short Message Service Parameters (SMSP)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 17 - Group Identifier Level 1
Service 19 - Service Provider Name
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 30 - Call Control by USIM
Service 31 - MO-SMS Control by USIM
Service 32 - RUN AT COMMAND command
Service 33 - shall be set to 1
Service 34 - Enabled Services Table
Service 35 - APN Control List (ACL)
Service 38 - GSM security context
Service 39 - CPBCCH Information
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 47 - Mailbox Dialling Numbers
Service 48 - Message Waiting Indication Status
Service 52 - Multimedia Messaging Service (MMS)
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

@@ -1,7 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
ADM=DDDDDDDD

View File

@@ -1,55 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmosim-gr1
Can't read AIDs from SIM -- SW match failed! Expected 9000 and got 9404.
ICCID: 1122334455667788990
IMSI: 001010000000102
GID1: Can't read file -- SW match failed! Expected 9000 and got 9404.
GID2: Can't read file -- SW match failed! Expected 9000 and got 9404.
SMSP: ffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Not available
Display HPLMN: False
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
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
AD: 000000
SIM Service Table: ff3fff0f0f0000030000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Done !

View File

@@ -1,13 +0,0 @@
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)

View File

@@ -1,5 +0,0 @@
MCC=001
MNC=01
IMSI=001010000000102
ADM_HEX=0123456789ABCDEF

View File

@@ -1,6 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102

View File

@@ -1,229 +0,0 @@
#!/bin/bash
# Utility to verify the functionality of pysim-prog.py
#
# (C) 2018 by Sysmocom s.f.m.c. GmbH
# All Rights Reserved
#
# Author: Philipp Maier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
PYSIM_PROG=../pySim-prog.py
PYSIM_READ=../pySim-read.py
TEMPFILE=temp.tmp
set -e
echo "pysim-test - a test program to test pysim-prog.py"
echo "================================================="
# Generate a list of the cards we expect to see by checking which .ok files
# are present
function gen_card_list {
N_CARDS=0
echo "Expecting to see the following cards:"
for I in *.data ; do
CARD_NAMES[$N_CARDS]=${I%.*}
CARD_SEEN[$N_CARDS]=0
N_CARDS=$((N_CARDS+1))
done
for I in $(seq 0 $((N_CARDS-1))); do
echo ${CARD_NAMES[$I]}
done
}
# Increment counter in card list for a specified card name (type)
function inc_card_list {
CARD_NAME=$1
for I in $(seq 0 $((N_CARDS-1))); do
if [ $CARD_NAME = ${CARD_NAMES[$I]} ]; then
CARD_SEEN[$I]=$((${CARD_NAMES[$I]}+1))
fi
done
}
# Check the card list, each card must be seen exactly one times
function check_card_list {
for I in $(seq 0 $((N_CARDS-1))); do
if [ ${CARD_SEEN[$I]} -ne 1 ]; then
echo "Error: Card ${CARD_NAMES[$I]} seen ${CARD_SEEN[$I]} times!"
exit 1
fi
done
echo "All cards seen -- everything ok!"
}
# Verify the contents of a card by reading them and then diffing against the
# previously created .ok file
function check_card {
TERMINAL=$1
CARD_NAME=$2
echo "Verifying card ..."
stat ./$CARD_NAME.ok > /dev/null
python $PYSIM_READ -p $TERMINAL > $TEMPFILE
set +e
CARD_DIFF=$(diff $TEMPFILE ./$CARD_NAME.ok)
set -e
if [ "$CARD_DIFF" != "" ]; then
echo "Card contents do not match the test data:"
echo "Expected: $CARD_NAME.ok"
echo "------------8<------------"
cat "$CARD_NAME.ok"
echo "------------8<------------"
echo "Got:"
echo "------------8<------------"
cat $TEMPFILE
echo "------------8<------------"
rm *.tmp
exit 1
fi
inc_card_list $CARD_NAME
echo "Card contents match the test data -- success!"
rm $TEMPFILE
}
# Read out the card using pysim-read and store the result as .ok file. This
# data will be used later in order to verify the results of our write tests.
function gen_ok_file {
TERMINAL=$1
CARD_NAME=$2
python $PYSIM_READ -p $TERMINAL > "$CARD_NAME.ok"
echo "Generated file: $CARD_NAME.ok"
echo "------------8<------------"
cat "$CARD_NAME.ok"
echo "------------8<------------"
}
# Find out the type (card name) of the card that is installed in the specified
# reader
function probe_card {
TERMINAL=$1
RESULT=$(timeout 5 $PYSIM_PROG -p $TERMINAL -T | cut -d ":" -f 2 | tail -n 1 | xargs)
echo $RESULT
}
# Read out all cards and store the results as .ok files
function gen_ok_files {
echo "== OK FILE GENERATION =="
for I in $(seq 0 $((N_TERMINALS-1))); do
echo "Probing card in terminal #$I"
CARD_NAME=$(probe_card $I)
if [ -z "$CARD_NAME" ]; then
echo "Error: Unresponsive card!"
exit 1
fi
echo "Card is of type: $CARD_NAME"
gen_ok_file $I $CARD_NAME
done
}
# Execute tests. Each card is programmed and the contents are checked
# afterwards.
function run_test {
for I in $(seq 0 $((N_TERMINALS-1))); do
echo "== EXECUTING TEST =="
echo "Probing card in terminal #$I"
CARD_NAME=$(probe_card $I)
if [ -z "$CARD_NAME" ]; then
echo "Error: Unresponsive card!"
exit 1
fi
echo "Card is of type: $CARD_NAME"
# Make sure some default data is set
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000001
MSISDN=6766266
ADM=00000000
ADM_HEX=""
ADM_OPT="-a"
source "$CARD_NAME.data"
if [ -n "$ADM_HEX" ]; then
ADM_OPT="-A"
ADM=$ADM_HEX
fi
python $PYSIM_PROG -p $I -t $CARD_NAME -o $OPC -k $KI -x $MCC -y $MNC -i $IMSI -s $ICCID --msisdn $MSISDN $ADM_OPT $ADM
check_card $I $CARD_NAME
echo ""
done
}
function usage {
echo "Options:"
echo "-n: number of card terminals"
echo "-o: generate .ok files"
}
# Make sure that the pathes to the python scripts always work, regardless from
# where the script is called.
CURDIR=$PWD
SCRIPTDIR=$(dirname $0)
cd $SCRIPTDIR
PYSIM_PROG=$(realpath $PYSIM_PROG)
PYSIM_READ=$(realpath $PYSIM_READ)
cd $CURDIR
OPT_N_TERMINALS=0
OPT_GEN_OK_FILES=0
while getopts ":hon:" OPT; do
case $OPT in
h)
usage
exit 0
;;
o)
OPT_GEN_OK_FILES=1
;;
n)
OPT_N_TERMINALS=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
N_TERMINALS=$OPT_N_TERMINALS
# Generate a list of available cards, if no explicit reader number is given
# then the number of cards will be used as reader number.
gen_card_list
if [ $N_TERMINALS -eq 0 ]; then
N_TERMINALS=$N_CARDS
fi
echo "Number of card terminals installed: $N_TERMINALS"
echo ""
if [ $OPT_GEN_OK_FILES -eq 1 ]; then
gen_ok_files
exit 0
else
run_test
check_card_list
exit 0
fi

View File

@@ -1,8 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
MSISDN=+77776336143
ADM=12345678

View File

@@ -1,7 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
ADM=DDDDDDDD